使用Spring Boot Security 实现多认证 手机号登录 微信扫码登录 微信扫码注册

这篇具有很好参考价值的文章主要介绍了使用Spring Boot Security 实现多认证 手机号登录 微信扫码登录 微信扫码注册。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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>

这样 前端就会显示二维码

security 实现扫码登录,springboot,微信扫码,spring security,spring boot,微信,后端,微信开放平台
然后就是微信扫码成功后重定向 也就是调用我们这个接口的回调

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 是否存在 存在的话 就说明是用户正在注册 让用户 输入 手机号 密码 和验证码注册
注册接口 如下

具体参数,其实无非就是你注册的手机号 还有密码 这个根据你的业务去写
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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • spring boot security使用jwt认证

    在前面的几篇文章中: spring boot security快速使用示例 spring boot security之前后端分离配置 spring boot security自定义认证 spring boot security验证码登录示例 基本对常用的基于cookie和session的认证使用场景都已覆盖。但是session属于有状态认证,本文给出一个无状态的认证:jwt认证示例。

    2024年02月12日
    浏览(47)
  • Spring Boot 如何使用 Spring Security 进行认证和授权

    在 Web 应用程序中,认证和授权是非常重要的功能。Spring Security 是一个基于 Spring 框架的强大的安全框架,它提供了完整的认证和授权解决方案,并且可以轻松地集成到 Spring Boot 应用程序中。本文将介绍如何在 Spring Boot 中使用 Spring Security 进行认证和授权,并提供示例代码。

    2024年02月11日
    浏览(41)
  • uniapp实现手机号一键登录功能

    1,第一步 2,第二步 创建应用要和项目uni-appid一致。 3,第三步 4,第四步 5,第五步 6,第六步 7,第七步 8,第八步 (实现代码) 一建登录步骤到此结束,欢迎大家讨论和指导,登录弹窗本文设置的是全屏‘fullScreen’,大家可根据需求编辑,弹窗只能在手机端app才能显示。

    2024年02月16日
    浏览(44)
  • 手机号加解密业务,通过aop实现

    序言: 在开发过程中因为功能的特殊性  需要对客户信息的手机号进行加密处理 然后存到数据库中,然而这个需求是项目中期加入,很多功能上已经使用了获取客户信息的方法,没法统一控制手机号加解密操作, 于是考虑使用 aop做环绕增强 对所有出参进行,解密操作(这里只

    2024年02月11日
    浏览(46)
  • 微信小程序: java实现获取手机号方式

    目录 1. 现在比较简单的方式 - 接口名 --- 功能描述 - 调用方式 --- HTTPS 调用 --- 第三方调用 --- 请求参数 --- 返回参数 2. 实现方式 1. 加入fastjson依赖  2. http请求类 3. Json串工具类 4.接口方法 3.另外介绍一点access_token 微信官方文档介绍:  getPhoneNumber --- 功能描述 该接口需配合手机

    2024年02月16日
    浏览(68)
  • Java实现对手机号、身份证号、护照号脱敏

    背景: 我们在项目中经常会需要用到用户的敏感信息,比如手机号、身份证号、护照号; 当数据需要在页面上进行展示的时候就需要进行脱敏,将其中几位变为 *。 官方文档: https://www.hutool.cn/docs/#/core/工具类/信息脱敏工具-DesensitizedUtil Hutool依赖: 代码实现: 执行结果:

    2024年02月15日
    浏览(40)
  • 怎么注册Google账号(使用国内手机号注册)

    记录一下如何用 国内的手机号 注册Google账号 Google官网(没有tizi是进不去的)点击登录 如图所示进入创建账号页面 你会发现,这样操作的话,国内的手机号是不允许被使用的。 点击浏览器右上角的 “三点” 选择 “语言” ,我们要做的就是 将浏览器的显示语言改为英文(

    2024年02月01日
    浏览(57)
  • uniapp 实现微信小程序手机号一键登录

    app 和 h5 手机号一键登录,参考文档:uni-app官网 以下是uniapp 实现微信小程序手机号一键登录 1、布局

    2024年02月03日
    浏览(56)
  • 能否通过手机号查询他人位置及技术实现(省流:不能)

    🍀作者简介:被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 🍁个人主页:红中 🫒每日emo:纪念我死去的爱情 🍂灵感来源:艺术源于生活,而高于生活 头部声明:如果您是来学怎么定位他人的,不好意思,这是违法行为,我不会,也不会

    2024年02月02日
    浏览(38)
  • vue项目表单使用正则过滤ip、手机号

    useFormValidate .js 手机号验证

    2024年02月03日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包