Spring Boot 3.x
Spring Security 5.7
Spring Redis
MyBatis plus
前端 Vue
前言
公司 最近有个新项目 使用单点登录 sso
百度了一圈 也没怎么找到微信扫码注册的功能于是自己写
- 需求就是 手机 + 密码登录
- 微信扫码登录
- 微信扫码注册
微信二维码 登录 和注册二合一 具体实现 稍后我会说
本教程将指导您如何使用Spring Boot和Spring Security 5.7来实现基于手机号密码登录的认证。通过本教程,您将学习如何配置Spring
Security,创建自定义的用户认证逻辑,并且使用手机号和密码进行登录。
准备工作
添加核心依赖
在您的Spring Boot项目的pom.xml文件中添加Spring Security依赖:
- weixin-java-mp
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--weixin-java-mp组件是一个基于Java语言开发的微信公众号开发工具包,是WxJava SDK在微信公众号场景的一个实现-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>${weixin.version}</version>
</dependency>
- application.yml 部分代码
wxopen:
openid: 开放平台网站openid
appid: 公众号appid
secret: 公众号凭据
key: 公众号密钥
# 扫码成功回调地址 这是你的回调接口 /code 文章后面会说
redirect_url: https://www.xxxx.com/code
配置实体类
@Configuration
public class WxMpServiceConfiguration {
@Value("${wxopen.appid}")
private String appId;
@Value("${wxopen.openid}")
private String openid;
@Value("${wxopen.secret}")
private String appSecret;
@Value("${wxopen.redirect_url}")
private String redirectUrl;
/**
* 定义WxMpService bean
*/
@Bean
public WxMpService wxMpService() {
WxMpService wxMpService = new WxMpServiceImpl();
WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
config.setAppId(openid);
config.setOauth2redirectUri(redirectUrl);
config.setSecret(appSecret);
wxMpService.setWxMpConfigStorage(config);
return wxMpService;
}
}
}
开始回归正题 实现基于手机号密码登录
获取微信授权二维码 登录 注册 我都使用一个二维码 无非就是加一个 标识来区分是 登录 还是注册 (mode)
/**
* @Desc 获取微信授权二维码 此接口只接收前端的请求(监听IP白名单)
* @Author Likefr
* @param mode (login) 登录 | (register) 注册
* @Date 2024/03/01 11:02
*/
@GetMapping("/qr/{mode}")
public ResultBean getAuthQr(@PathVariable("mode") String mode) {
if (mode == null) {
return ResultBean.error(ResultBeanEnum.BIND_ERROR);
}
Map<String, Object> map = new HashMap<>();
map.put("appid", weChatConfig.getOpenId());
map.put("scope", "snsapi_login");
String uuid = UUID.randomUUID().toString();
String backUrl = "https://xxxxx.com/wechat/code?handlerType=" + mode + "&uuid=" + uuid;
map.put("redirect_uri", backUrl);
map.put("state", uuid);
return ResultBean.success(map);
}
可能会问 backUrl 这个地址是哪个地址? 其实就是一个 我们springboot 的一个接口
@RequestMapping(value = "/code", method = {RequestMethod.GET, RequestMethod.POST})
这个接口 放在下面具体 往下滑 !
前端 发送 @GetMapping(“/qr/{mode}”) 接口 来获取二维码
比如 :
- 我请求 /qr/login 我就能获取登录的二维码
- 我请求 /qr/register 我就能获取注册的二维码
我这边贴上部分代码 前端需要引入才能 使用 new WxLogin
<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
<script src="https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
// springboot 后端接口返回 (@GetMapping("/qr/{mode}"))
data.data = return ResultBean.success(map);
var obj = new WxLogin({
self_redirect: false,
id: 'weixinLogin', // 需要显示的容器id
appid: data.data.appid, // 公众号appid wx*******
scope: data.data.scope, // 网页默认即可
redirect_uri: data.data.redirect_uri, // 授权成功后回调的url
state: data.data.state, // 可设置为简单的随机数加session用来校验
style: 'black', // 提供"black"、"white"可选。二维码的样式
href: '' // 外部css文件url,需要https
})
return
}
<div id="weixinLogin"></div>
这样 前端就会显示二维码
然后就是微信扫码成功后重定向 也就是调用我们这个接口的回调
tips 还记得我们刚才 前端 调用的 @GetMapping(“/qr/{mode}”) 接口参数吗
mode 这个参数 就是用来区分 我是用来 登录 还是注册(handlerType)
这点很重要 !!!
/**
* @Desc 该接口只接收微信回调请求获取code
* @Author Likefr
* @Date 2024/2/28 16:05
*/
@RequestMapping(value = "/code", method = {RequestMethod.GET, RequestMethod.POST})
public void getCallBackCode(HttpServletRequest request, HttpServletResponse response) throws IOException, WxErrorException {
String code = request.getParameter("code"); //获取code
String handlerType = request.getParameter("handlerType"); //获取二维码类型
// 登录
if ("login".equals(handlerType)) {
response.sendRedirect("https://www.xxxx.com/#/index?uuid=" + code);
// 注册
} else if ("register".equals(handlerType)) {
response.sendRedirect("https://www.xxxx.com/login/#/?uuid=" + code);
}
}
如果是login 我跳到前端首页
if ("login".equals(handlerType)) {
response.sendRedirect("https://www.xxxx.com/#/index?uuid=" + code);
} else if ("register".equals(handlerType)) {
response.sendRedirect(""https://www.xxxx.com/login/#/?uuid=" + code);
}
这段代码 就是微信帮我们跳转到前端 的具体某个页面
如果是扫码登录response.sendRedirect("https://www.xxxx.com/#/login?uuid=" + code);
前端 vue框架 created 钩子函数 里面判断 链接 uuid=" + code 是否存在code 存在则 发送/wechat/login接口
// spring security 拦截 POST /wechat/login 请求
RequestMatcher matcher = new AntPathRequestMatcher("/wechat/login", "POST", true);
然后经过 WxQrAuthenticationFilter WxQrAuthenticationFilter 获取 刚才 链接上uuid 字段 的code值 获取到 uuid后 封装一个 Authentication authentication = new WxQrAuthenticationToken(userForm.getUuid()); 对象
在 调用 WxQrCodeAuthenticationProvider 最后认证
以下是我的 security 微信扫码登录认证器
/**
* @author Likefr
* 基于用户名(手机号)、密码、验证码的登录拦截器配置类
*/
public class WxQrLoginConfigurer extends AbstractHttpConfigurer<WxQrLoginConfigurer, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) {
// 拦截 POST /login 请求
RequestMatcher matcher = new AntPathRequestMatcher("/wechat/login", "POST", true);
SecurityUserDetailsService userDetailService = builder.getSharedObject(ApplicationContext.class).getBean(SecurityUserDetailsService.class);
RedisTemplate redisTemplate = builder.getSharedObject(ApplicationContext.class).getBean(RedisTemplate.class);
WxMpService wxMpService = builder.getSharedObject(ApplicationContext.class).getBean(WxMpService.class);
AuthenticationManager localAuthManager = builder.getSharedObject(AuthenticationManager.class);
WxQrAuthenticationFilter filter = new WxQrAuthenticationFilter(matcher, localAuthManager);
filter.setAuthenticationSuccessHandler(new LoginSuccessHandler(userDetailService, redisTemplate));
filter.setAuthenticationFailureHandler(new LoginFailHandler());
// 务必注意这里与配置类中声明的先后顺序
builder.authenticationProvider(new WxQrCodeAuthenticationProvider(userDetailService, redisTemplate, wxMpService))
.addFilterBefore(filter, AuthenticationTokenFilter.class);
}
}
@Slf4j
public class WxQrCodeAuthenticationProvider implements AuthenticationProvider {
private SecurityUserDetailsService userDetailsService;
private RedisTemplate<String, Object> redisTemplate;
private WxMpService wxMpService;
public WxQrCodeAuthenticationProvider(SecurityUserDetailsService userService, RedisTemplate redisTemplate, WxMpService wxMpService) {
this.userDetailsService = userService;
this.redisTemplate = redisTemplate;
this.wxMpService = wxMpService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
WxQrAuthenticationToken authenticationToken = (WxQrAuthenticationToken) authentication;
// 获取前端的
String uuid = authenticationToken.getUuid();
// 根据uuid 获取微信用户 (核心)
SecutityUser userDetails = checkUuid(uuid);
if (Objects.isNull(userDetails)) {
throw new InternalAuthenticationServiceException(" 无效的 uuid!");
}
// 用户状态校验
if (!userDetails.isEnabled() || !userDetails.isAccountNonLocked() || !userDetails.isAccountNonExpired()) {
throw new LockedException("用户已禁用,请联系管理员启用");
}
WxQrAuthenticationToken authenticationResult = new WxQrAuthenticationToken(userDetails, uuid, new ArrayList<>());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return WxQrAuthenticationToken.class.isAssignableFrom(authentication);
}
private SecutityUser checkUuid(String uuid) {
WxOAuth2AccessToken accessToken = null;
WxOAuth2UserInfo userInfo;
try {
accessToken = wxMpService.getOAuth2Service().getAccessToken(uuid);
userInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, "zh_CN");
} catch (WxErrorException e) {
throw new CredentialsExpiredException(e.getMessage().toString());
}
// 根据微信用户id 查找你数据库的用户 这边我不贴代码了 很简单
SecutityUser userDetails = userDetailsService.loadUserByUsername(userInfo.getOpenid());
return userDetails;
}
}
/**
* 二维码登录拦截器
*/
public class WxQrAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public WxQrAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher, AuthenticationManager authenticationManager) {
super(requiresAuthenticationRequestMatcher, authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
LoginVo userForm = HttpRequestUtil.getBodyJson(request);
logger.info(userForm.toString());
Authentication authentication = new WxQrAuthenticationToken(userForm.getUuid());
// authenticate 会执行 SecutityUserDetailsService
return this.getAuthenticationManager().authenticate(authentication);
}
}
基于手机号 + 密码的认证登录
/**
* @author Likefr
* 基于用户名(手机号)、验证码的登录拦截器配置类
*/
public class PhoneLoginConfigurer extends AbstractHttpConfigurer<PhoneLoginConfigurer, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) {
// 拦截 POST /login 请求
RequestMatcher matcher = new AntPathRequestMatcher("/phone/doLogin", "POST", true);
SecurityUserDetailsService userDetailService = builder.getSharedObject(ApplicationContext.class).getBean(SecurityUserDetailsService.class);
RedisTemplate redisTemplate = builder.getSharedObject(ApplicationContext.class).getBean(RedisTemplate.class);
AuthenticationManager localAuthManager = builder.getSharedObject(AuthenticationManager.class);
PhoneAuthenticationFilter filter = new PhoneAuthenticationFilter(matcher, localAuthManager);
filter.setAuthenticationSuccessHandler(new LoginSuccessHandler(userDetailService, redisTemplate));
filter.setAuthenticationFailureHandler(new LoginFailHandler());
// 委托认证autheticate AuthenticationProvider
builder.authenticationProvider(new PhoneAuthenticationProvider(userDetailService, redisTemplate))
.addFilterBefore(filter, AuthenticationTokenFilter.class);
}
}
/**
* @author Likefr
* 基于用户名(手机号)、密码的认证处理器
*/
@Slf4j
public class PhoneAuthenticationProvider implements AuthenticationProvider {
private final SecurityUserDetailsService userDetailService;
private RedisTemplate<String, Object> redisTemplate;
public PhoneAuthenticationProvider(SecurityUserDetailsService userDetailService, RedisTemplate redisTemplate) {
this.userDetailService = userDetailService;
this.redisTemplate = redisTemplate;
}
/**
* 验证主逻辑
*/
/*
provider = class ProviderManager implements AuthenticationManager
result = provider.authenticate(authentication);
* */
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
PhoneAuthenticationToken authToken = (PhoneAuthenticationToken) authentication;
if (authToken.getPhone() == null) {
throw new UsernameNotFoundException("未输入手机号");
}
if (Objects.isNull(authToken.getCredentials())) {
throw new BadCredentialsException("密码不能为空");
}
// 加载用户详情
// log.info("authToken.getCredentials() {} {}", authToken.getCredentials(), authToken.getPhone());
SecutityUser userDetails = userDetailService.loadUserByUsername(authToken.getPhone());
if (Objects.isNull(userDetails.getSysUser().getPassword())) {
throw new BadCredentialsException("当前用户未设置密码");
}
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
if (!bCryptPasswordEncoder.matches(authToken.getCredentials(), userDetails.getSysUser().getPassword())) {
throw new BadCredentialsException("用户名或密码错误,请重新输入");
}
// 检查用户状态
if (!userDetails.isEnabled() || !userDetails.isAccountNonLocked() || !userDetails.isAccountNonExpired()) {
throw new LockedException("用户处于禁用状态,请联系管理员启用");
}
// 生成 JWT 并存储
return new PhoneAuthenticationToken(userDetails, authToken.getCredentials(), authToken.getAuthorities());
}
/**
* 当类型为PasswordAuthenticationToken的认证实体进入时才走此Provider
*/
//
@Override
public boolean supports(Class<?> authentication) {
return PhoneAuthenticationToken.class.isAssignableFrom(authentication);
}
/**
* @author Likefr
* 用户名密码登录拦截器
* 主要处理两个事情 1 @params request 提取前端 提交的表单信息
* 2 然后封装成 基于 手机号 验证码 实现的Authentication 对象
* retuerns Authentication
*/
public class PhoneAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public PhoneAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher, AuthenticationManager authenticationManager) {
super(requiresAuthenticationRequestMatcher, authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
LoginVo userForm = HttpRequestUtil.getBodyJson(request);
Authentication authentication = new PhoneAuthenticationToken(userForm.getNickname(), userForm.getPassword());
// authenticate委托认证 交由 DaoAuthenticationProvider
return this.getAuthenticationManager().authenticate(authentication);
}
}
增加 Token 拦截器配置
/**
* Token拦截器配置类
* TokenAuthenticationFilter 负责处理我们携带了jwt token的请求。认证工作主要由它负责。
*/
public class TokenAuthenticationConfigurer extends AbstractHttpConfigurer<TokenAuthenticationConfigurer, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) {
RedisTemplate RedisTemplate = builder.getSharedObject(ApplicationContext.class).getBean(RedisTemplate.class);
AccessDeniedHandler accessDeniedHandler = builder.getSharedObject(ApplicationContext.class).getBean(AccessDeniedHandler.class);
builder.addFilterBefore(new AuthenticationTokenFilter(RedisTemplate, accessDeniedHandler), UsernamePasswordAuthenticationFilter.class);
}
}
JWT 拦截处理实现
/**
* JWT Token认证拦截器 每次请求接口会先解析jwt
* 用户 判断是否存在token
* token 是否有效
* token 是否过期
*/
public class AuthenticationTokenFilter extends OncePerRequestFilter {
// token 存在redis 里边 不存在则已退出登录
private RedisTemplate redisTemplate;
public AuthenticationTokenFilter(RedisTemplate redisTemplate,
AccessDeniedHandler accessDeniedHandler) {
this.redisTemplate = redisTemplate;
this.accessDeniedHandler = accessDeniedHandler;
}
private AccessDeniedHandler accessDeniedHandler;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
// 未登录 如果字符串不为 null 值,并且不含有空白字符序列,并且字符序列的长度大于 0 ,则返回 true
if (!StringUtils.hasText(token)) {
filterChain.doFilter(request, response);
return;
}
token = token.replace("bearer ", "");
// 判断jwt 是否过期
Boolean expire = JWTUtils.isExpire(token);
if (expire == null) {
accessDeniedHandler.handle(request, response, new AccessDeniedException(ResultBeanEnum.JWT_ERROR.getMeassage()));
return;
}
if (expire) {
// token 过期
filterChain.doFilter(request, response);
return;
}
Boolean redisJwtExists = redisTemplate.hasKey("loginToken:" + token);
SecutityUser userPojo = JWTUtils.checkToken(token);
if (userPojo != null && redisJwtExists) {
// 将解析的jwt 写入上下文 以供全局使用
PhoneAuthenticationToken authenticationToken = new PhoneAuthenticationToken(userPojo, token, userPojo.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// todo 实现jwt 无感续签
// redisCacheUtil.setExpire(TokenConstant.TOKEN_REDIS_PREFIX + token, TokenConstant.TOKEN_EXPIRE_TIME, TokenConstant.TOKEN_EXPIRE_TIME_UNIT);
}
filterChain.doFilter(request, response);
}
}
接下来就是最重要的配置 security了 将我们前面的拦截器 配置进去
/**
* @version: java version 17
* @Author: Likefr
* @description:
* @date: 2024-01-20 11:44
*/
@Configuration
/*
* 从Spring Security 4.0开始,不推荐使用@EnableWebMvcSecurity 。 replace是@EnableWebSecurity将决定添加基于类path的Spring MVCfunction。
为了使Spring Security与Spring MVC集成,将 @EnableWebSecurity 注释 添加 到您的configuration中。
* */
@EnableWebSecurity
public class WebSecutityConfig {
@Autowired
private LogoutSuccessHanlder logoutHandler;
@Autowired
private LoginFailHandler loginFailHandler;
@Autowired
AuthenticationExceptionHandler authenticationFailHandler;
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//关闭csrf
http.csrf().disable()
//自定义登录接口 禁用 FormLoginConfigurer 默认登录实现
.formLogin().disable()
.exceptionHandling().authenticationEntryPoint(authenticationFailHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
.authorizeHttpRequests(authorize -> authorize
// 放行手机号密码登录接口
.requestMatchers("/phone/doLogin").permitAll()
// 放行用户注册接口
.requestMatchers("/sysUser/register").permitAll()
// 微信扫码回调(这个很重要)
.requestMatchers("/code/**").permitAll()
.requestMatchers("/wechat/**").permitAll()
.anyRequest().authenticated())
.formLogin().failureHandler(loginFailHandler).and()
.logout().logoutUrl("/logout").logoutSuccessHandler(logoutHandler).and()
// 务必注意这里的先后顺序,否则会报NULL异常
// token 拦截 我使用的是jwt
.apply(new TokenAuthenticationConfigurer()).and()
// 手机号登录拦截器
.apply(new PhoneLoginConfigurer()).and()
// 微信扫码登录拦截器
.apply(new WxQrLoginConfigurer());
return http.build();
}
}
大致说下上面代码
需要注意的是放行登录 注册 接口 还有获取微信二维码的接口 如果不放行 你的登录或者注册 没法访问 (
没登陆当然无法访问了,熟悉security的都知道)
// token 拦截 我使用的是jwt
.apply(new TokenAuthenticationConfigurer()).and()
// 手机号登录拦截器
.apply(new PhoneLoginConfigurer()).and()
// 微信扫码登录拦截器
.apply(new WxQrLoginConfigurer());
这三行代码是在配置 Spring Security 的过滤器链中应用了三个自定义的配置器(configurer)。这些配置器定义了特定的认证方式或者处理特定类型的认证请求
我简单说下:
-
.apply(new TokenAuthenticationConfigurer())
: 这一行代码声明了一个名为TokenAuthenticationConfigurer
的配置器,这个配置器我实现了拦截基于令牌(JWT)的身份验证 -
.apply(new PhoneLoginConfigurer())
: 这一行代码声明了一个名为PhoneLoginConfigurer
的配置器,准确的说是手机号
密码登录这个配置器用于配置手机登录认证方式,包含手机号 + 密码登录 -
.apply(new WxQrLoginConfigurer())
: 这一行代码应用了一个名为WxQrLoginConfigurer
的配置器。根据名称应该可以知道,是一个前端扫码微信二维码登录,
这个配置了微信扫码登录认证方式,涉及对接微信授权。
这三个配置器的先后顺序非常重要,因为它们可能会相互依赖或者有先后执行的逻辑,说这里的顺序可能影响到程序的正确性。如果这些配置器之间有先后顺序的要求,而没有按照要求配置,就有可能出现空指针异常(NULL
异常)或其他配置错误。在使用这些配置器时务必注意它们的先后顺序
再就是微信扫码注册实现 还记得 我们前端调用的这个接口吗?
@GetMapping("/qr/{mode}")
当mode = register时 我会重定向到前端登录页
response.sendRedirect("https://www.xxxx.com/login/#/?uuid=" + code);
就是我会在注册页Login.vue created 钩子函数里面
判断 ?uuid=" + code 是否存在 存在的话 就说明是用户正在注册 让用户 输入 手机号 密码 和验证码注册
注册接口 如下文章来源:https://www.toymoban.com/news/detail-846032.html
具体参数,其实无非就是你注册的手机号 还有密码 这个根据你的业务去写
loginVo 实体类包含uuid这个字段 这个u id的值其实就是微信扫码登录成功返回 code
** response.sendRedirect(“https://www.xxxx.com/login/#/?uuid=” + code);**
所以前端要解析出 uuid 并在注册的时候把这个值传过来,获取微信用户文章来源地址https://www.toymoban.com/news/detail-846032.html
public ResultBean addUser(LoginVo loginVo) {
// 手机验证码是否为空
if (StringUtils.isEmpty(loginVo.getCode())) {
return ResultBean.error(ResultBeanEnum.CODE_NOTNULL_ERROR); // 验证码为空
}
// 校验验证码
String redisCode = (String) redisTemplate.opsForValue().get(RedisKeyConstant.SMSCODE.cacheKey + loginVo.getPhone());
if (!loginVo.getCode().equals(redisCode)) {
return ResultBean.error(ResultBeanEnum.CODE_ERROR);
}
QueryWrapper<SysUser> wrapper = new QueryWrapper<SysUser>();
// 判断手机号是否注册过
wrapper.eq("phone", loginVo.getPhone());
long count = count(wrapper);
if (count > 0) {
return ResultBean.error(ResultBeanEnum.PHONE_OTHER_ACCOUNTS_ERROR);
}
// 获取微信id
WxOAuth2AccessToken accessToken = null;
WxOAuth2UserInfo userInfo;
try {
log.info("loginVo.getUuid():{}", loginVo.getUuid());
accessToken = wxMpService.getOAuth2Service().getAccessToken(loginVo.getUuid());
// log.info("accessToken- {}", accessToken);
userInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, "zh_CN");
} catch (WxErrorException e) {
throw new CredentialsExpiredException(e.getMessage().toString());
}
wrapper = new QueryWrapper<SysUser>();
wrapper.eq("openid", userInfo.getOpenid());
// 判断微信是否已经绑定了其他手机号
long wxCount = count(wrapper);
if (wxCount > 0) {
return ResultBean.error(ResultBeanEnum.WECHAT_OTHER_ACCOUNTS_ERROR);
}
SysUser sysUser = new SysUser();
sysUser.setNickname(loginVo.getPhone());
sysUser.setPhone(loginVo.getPhone());
sysUser.setOpenid(userInfo.getOpenid());
sysUser.setCreateTime(LocalDateTime.now());
sysUser.setState(true);
if (!ObjectUtils.isEmpty(loginVo.getPassword())) {
String password = new BCryptPasswordEncoder().encode(loginVo.getPassword());
sysUser.setPassword(password);
}
boolean save = save(sysUser);
if (save) {
// 注册成功
return ResultBean.success();
}
return ResultBean.error(ResultBeanEnum.USER_ADD_ERROR);
}
到了这里,关于使用Spring Boot Security 实现多认证 手机号登录 微信扫码登录 微信扫码注册的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!