国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

使用Spring Boot Security 實(shí)現(xiàn)多認(rèn)證 手機(jī)號(hào)登錄 微信掃碼登錄 微信掃碼注冊(cè)

這篇具有很好參考價(jià)值的文章主要介紹了使用Spring Boot Security 實(shí)現(xiàn)多認(rèn)證 手機(jī)號(hào)登錄 微信掃碼登錄 微信掃碼注冊(cè)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

Spring Boot 3.x
Spring Security 5.7
Spring Redis
MyBatis plus
前端 Vue

前言

公司 最近有個(gè)新項(xiàng)目 使用單點(diǎn)登錄 sso
百度了一圈 也沒(méi)怎么找到微信掃碼注冊(cè)的功能于是自己寫(xiě)

  • 需求就是 手機(jī) + 密碼登錄
  • 微信掃碼登錄
  • 微信掃碼注冊(cè)

微信二維碼 登錄 和注冊(cè)二合一 具體實(shí)現(xiàn) 稍后我會(huì)說(shuō)

本教程將指導(dǎo)您如何使用Spring Boot和Spring Security 5.7來(lái)實(shí)現(xiàn)基于手機(jī)號(hào)密碼登錄的認(rèn)證。通過(guò)本教程,您將學(xué)習(xí)如何配置Spring
Security,創(chuàng)建自定義的用戶(hù)認(rèn)證邏輯,并且使用手機(jī)號(hào)和密碼進(jìn)行登錄。

準(zhǔn)備工作

添加核心依賴(lài)

在您的Spring Boot項(xiàng)目的pom.xml文件中添加Spring Security依賴(lài):

  • weixin-java-mp

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
        <!--weixin-java-mp組件是一個(gè)基于Java語(yǔ)言開(kāi)發(fā)的微信公眾號(hào)開(kāi)發(fā)工具包,是WxJava SDK在微信公眾號(hào)場(chǎng)景的一個(gè)實(shí)現(xiàn)-->
<dependency>
	<groupId>com.github.binarywang</groupId>
	<artifactId>weixin-java-mp</artifactId>
	<version>${weixin.version}</version>
</dependency>
  • application.yml 部分代碼
wxopen:
  openid: 開(kāi)放平臺(tái)網(wǎng)站openid
  appid: 公眾號(hào)appid
  secret: 公眾號(hào)憑據(jù)
  key: 公眾號(hào)密鑰
  #  掃碼成功回調(diào)地址 這是你的回調(diào)接口 /code 文章后面會(huì)說(shuō)
  redirect_url: https://www.xxxx.com/code

配置實(shí)體類(lèi)

@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;
    }
}


}

開(kāi)始回歸正題 實(shí)現(xiàn)基于手機(jī)號(hào)密碼登錄

獲取微信授權(quán)二維碼 登錄 注冊(cè) 我都使用一個(gè)二維碼 無(wú)非就是加一個(gè) 標(biāo)識(shí)來(lái)區(qū)分是 登錄 還是注冊(cè) (mode)

/**
 * @Desc 獲取微信授權(quán)二維碼 此接口只接收前端的請(qǐng)求(監(jiān)聽(tīng)I(yíng)P白名單)
 * @Author Likefr
 * @param mode (login) 登錄 | (register) 注冊(cè)
 * @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);
}

可能會(huì)問(wèn) backUrl 這個(gè)地址是哪個(gè)地址? 其實(shí)就是一個(gè) 我們springboot 的一個(gè)接口

@RequestMapping(value = "/code", method = {RequestMethod.GET, RequestMethod.POST})

這個(gè)接口 放在下面具體 往下滑 !


前端 發(fā)送 @GetMapping(“/qr/{mode}”) 接口 來(lái)獲取二維碼
比如 :

  • 我請(qǐng)求 /qr/login 我就能獲取登錄的二維碼
  • 我請(qǐng)求 /qr/register 我就能獲取注冊(cè)的二維碼

我這邊貼上部分代碼 前端需要引入才能 使用 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, // 公眾號(hào)appid wx*******
          scope: data.data.scope, // 網(wǎng)頁(yè)默認(rèn)即可
          redirect_uri: data.data.redirect_uri, // 授權(quán)成功后回調(diào)的url
          state: data.data.state, // 可設(shè)置為簡(jiǎn)單的隨機(jī)數(shù)加session用來(lái)校驗(yàn)
          style: 'black', // 提供"black"、"white"可選。二維碼的樣式
          href: '' // 外部css文件url,需要https
        })
        return
      }
          <div id="weixinLogin"></div>

這樣 前端就會(huì)顯示二維碼

security 實(shí)現(xiàn)掃碼登錄,springboot,微信掃碼,spring security,spring boot,微信,后端,微信開(kāi)放平臺(tái)
然后就是微信掃碼成功后重定向 也就是調(diào)用我們這個(gè)接口的回調(diào)

tips 還記得我們剛才 前端 調(diào)用的 @GetMapping(“/qr/{mode}”) 接口參數(shù)嗎
mode 這個(gè)參數(shù) 就是用來(lái)區(qū)分 我是用來(lái) 登錄 還是注冊(cè)(handlerType)

這點(diǎn)很重要 ?。?!

/**
 * @Desc 該接口只接收微信回調(diào)請(qǐng)求獲取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");  //獲取二維碼類(lèi)型
    // 登錄 
    if ("login".equals(handlerType)) {
        response.sendRedirect("https://www.xxxx.com/#/index?uuid=" + code);
        // 注冊(cè)
    } else if ("register".equals(handlerType)) {
        response.sendRedirect("https://www.xxxx.com/login/#/?uuid=" + code);
    }
}

如果是login 我跳到前端首頁(yè)

   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);
        }

這段代碼 就是微信幫我們跳轉(zhuǎn)到前端 的具體某個(gè)頁(yè)面
如果是掃碼登錄
response.sendRedirect("https://www.xxxx.com/#/login?uuid=" + code);

前端 vue框架 created 鉤子函數(shù) 里面判斷 鏈接 uuid=" + code 是否存在code 存在則 發(fā)送/wechat/login接口

    // spring security 攔截 POST /wechat/login 請(qǐng)求
    RequestMatcher matcher = new AntPathRequestMatcher("/wechat/login", "POST", true);

然后經(jīng)過(guò) WxQrAuthenticationFilter WxQrAuthenticationFilter 獲取 剛才 鏈接上uuid 字段 的code值 獲取到 uuid后 封裝一個(gè) Authentication authentication = new WxQrAuthenticationToken(userForm.getUuid()); 對(duì)象

在 調(diào)用 WxQrCodeAuthenticationProvider 最后認(rèn)證

以下是我的 security 微信掃碼登錄認(rèn)證器

/**
 * @author Likefr
 * 基于用戶(hù)名(手機(jī)號(hào))、密碼、驗(yàn)證碼的登錄攔截器配置類(lèi)
 */
public class WxQrLoginConfigurer extends AbstractHttpConfigurer<WxQrLoginConfigurer, HttpSecurity> {
    @Override
    public void configure(HttpSecurity builder) {
        // 攔截 POST /login 請(qǐng)求
        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());
        // 務(wù)必注意這里與配置類(lèi)中聲明的先后順序
        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();
        // 根據(jù)uuid 獲取微信用戶(hù) (核心)
        SecutityUser userDetails = checkUuid(uuid);

        if (Objects.isNull(userDetails)) {
            throw new InternalAuthenticationServiceException(" 無(wú)效的 uuid!");
        }
        // 用戶(hù)狀態(tài)校驗(yàn)
        if (!userDetails.isEnabled() || !userDetails.isAccountNonLocked() || !userDetails.isAccountNonExpired()) {
            throw new LockedException("用戶(hù)已禁用,請(qǐng)聯(lián)系管理員啟用");
        }
        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());
        }
//      根據(jù)微信用戶(hù)id 查找你數(shù)據(jù)庫(kù)的用戶(hù) 這邊我不貼代碼了 很簡(jiǎn)單 
        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 會(huì)執(zhí)行 SecutityUserDetailsService
        return this.getAuthenticationManager().authenticate(authentication);
    }
}

基于手機(jī)號(hào) + 密碼的認(rèn)證登錄


/**
 * @author Likefr
 * 基于用戶(hù)名(手機(jī)號(hào))、驗(yàn)證碼的登錄攔截器配置類(lèi)
 */
public class PhoneLoginConfigurer extends AbstractHttpConfigurer<PhoneLoginConfigurer, HttpSecurity> {
    @Override
    public void configure(HttpSecurity builder) {
        // 攔截 POST /login 請(qǐng)求
        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());
        // 委托認(rèn)證autheticate AuthenticationProvider
        builder.authenticationProvider(new PhoneAuthenticationProvider(userDetailService, redisTemplate))
                .addFilterBefore(filter, AuthenticationTokenFilter.class);
    }
}


/**
 * @author Likefr
 * 基于用戶(hù)名(手機(jī)號(hào))、密碼的認(rèn)證處理器
 */
@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;
    }

    /**
     * 驗(yàn)證主邏輯
     */
    /*
   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("未輸入手機(jī)號(hào)");
        }
        if (Objects.isNull(authToken.getCredentials())) {
            throw new BadCredentialsException("密碼不能為空");
        }
        // 加載用戶(hù)詳情
//        log.info("authToken.getCredentials() {} {}", authToken.getCredentials(), authToken.getPhone());

        SecutityUser userDetails = userDetailService.loadUserByUsername(authToken.getPhone());
        if (Objects.isNull(userDetails.getSysUser().getPassword())) {
            throw new BadCredentialsException("當(dāng)前用戶(hù)未設(shè)置密碼");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bCryptPasswordEncoder.matches(authToken.getCredentials(), userDetails.getSysUser().getPassword())) {
            throw new BadCredentialsException("用戶(hù)名或密碼錯(cuò)誤,請(qǐng)重新輸入");
        }
        // 檢查用戶(hù)狀態(tài)
        if (!userDetails.isEnabled() || !userDetails.isAccountNonLocked() || !userDetails.isAccountNonExpired()) {
            throw new LockedException("用戶(hù)處于禁用狀態(tài),請(qǐng)聯(lián)系管理員啟用");
        }

        // 生成 JWT 并存儲(chǔ)
        return new PhoneAuthenticationToken(userDetails, authToken.getCredentials(), authToken.getAuthorities());
    }


    /**
     * 當(dāng)類(lèi)型為PasswordAuthenticationToken的認(rèn)證實(shí)體進(jìn)入時(shí)才走此Provider
     */
//
    @Override
    public boolean supports(Class<?> authentication) {
        return PhoneAuthenticationToken.class.isAssignableFrom(authentication);
    }

/**
 * @author Likefr
 * 用戶(hù)名密碼登錄攔截器
 * 主要處理兩個(gè)事情 1 @params request 提取前端 提交的表單信息
 * 2 然后封裝成 基于 手機(jī)號(hào) 驗(yàn)證碼 實(shí)現(xiàn)的Authentication 對(duì)象
 * 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委托認(rèn)證 交由 DaoAuthenticationProvider
        return this.getAuthenticationManager().authenticate(authentication);
    }
}

增加 Token 攔截器配置

/**
 * Token攔截器配置類(lèi)
 * TokenAuthenticationFilter 負(fù)責(zé)處理我們攜帶了jwt token的請(qǐng)求。認(rèn)證工作主要由它負(fù)責(zé)。
 */
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 攔截處理實(shí)現(xiàn)


/**
 * JWT Token認(rèn)證攔截器 每次請(qǐng)求接口會(huì)先解析jwt
 * 用戶(hù) 判斷是否存在token
 * token 是否有效
 * token 是否過(guò)期
 */
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 值,并且不含有空白字符序列,并且字符序列的長(zhǎng)度大于 0 ,則返回 true
        if (!StringUtils.hasText(token)) {
            filterChain.doFilter(request, response);
            return;
        }
        token = token.replace("bearer ", "");

        // 判斷jwt 是否過(guò)期
        Boolean expire = JWTUtils.isExpire(token);
        if (expire == null) {
            accessDeniedHandler.handle(request, response, new AccessDeniedException(ResultBeanEnum.JWT_ERROR.getMeassage()));
            return;
        }
        if (expire) {
            // token 過(guò)期
            filterChain.doFilter(request, response);
            return;
        }
        Boolean redisJwtExists = redisTemplate.hasKey("loginToken:" + token);
        SecutityUser userPojo = JWTUtils.checkToken(token);
        if (userPojo != null && redisJwtExists) {
            // 將解析的jwt 寫(xiě)入上下文 以供全局使用
            PhoneAuthenticationToken authenticationToken = new PhoneAuthenticationToken(userPojo, token, userPojo.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            // todo 實(shí)現(xiàn)jwt 無(wú)感續(xù)簽
//      redisCacheUtil.setExpire(TokenConstant.TOKEN_REDIS_PREFIX + token, TokenConstant.TOKEN_EXPIRE_TIME, TokenConstant.TOKEN_EXPIRE_TIME_UNIT);
        }

        filterChain.doFilter(request, response);
    }
}


接下來(lái)就是最重要的配置 security了 將我們前面的攔截器 配置進(jìn)去


/**
 * @version: java version 17
 * @Author: Likefr
 * @description:
 * @date: 2024-01-20 11:44
 */

@Configuration

/*
* 從Spring Security 4.0開(kāi)始,不推薦使用@EnableWebMvcSecurity 。 replace是@EnableWebSecurity將決定添加基于類(lèi)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 {
        //關(guān)閉csrf
        http.csrf().disable()
                //自定義登錄接口 禁用 FormLoginConfigurer 默認(rèn)登錄實(shí)現(xiàn)
                .formLogin().disable()
                .exceptionHandling().authenticationEntryPoint(authenticationFailHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
                .authorizeHttpRequests(authorize -> authorize
                        // 放行手機(jī)號(hào)密碼登錄接口
                        .requestMatchers("/phone/doLogin").permitAll()
                        // 放行用戶(hù)注冊(cè)接口
                        .requestMatchers("/sysUser/register").permitAll()
                        // 微信掃碼回調(diào)(這個(gè)很重要)
                        .requestMatchers("/code/**").permitAll()
                        .requestMatchers("/wechat/**").permitAll()
                        .anyRequest().authenticated())
                .formLogin().failureHandler(loginFailHandler).and()
                .logout().logoutUrl("/logout").logoutSuccessHandler(logoutHandler).and()
                // 務(wù)必注意這里的先后順序,否則會(huì)報(bào)NULL異常
                // token 攔截 我使用的是jwt
                .apply(new TokenAuthenticationConfigurer()).and()
                // 手機(jī)號(hào)登錄攔截器
                .apply(new PhoneLoginConfigurer()).and()
                // 微信掃碼登錄攔截器
                .apply(new WxQrLoginConfigurer());
        return http.build();
    }
}

大致說(shuō)下上面代碼

需要注意的是放行登錄 注冊(cè) 接口 還有獲取微信二維碼的接口 如果不放行 你的登錄或者注冊(cè) 沒(méi)法訪問(wèn) (
沒(méi)登陸當(dāng)然無(wú)法訪問(wèn)了,熟悉security的都知道)

                // token 攔截 我使用的是jwt
                .apply(new TokenAuthenticationConfigurer()).and()
                        // 手機(jī)號(hào)登錄攔截器
                        .apply(new PhoneLoginConfigurer()).and()
                        // 微信掃碼登錄攔截器
                        .apply(new WxQrLoginConfigurer());

這三行代碼是在配置 Spring Security 的過(guò)濾器鏈中應(yīng)用了三個(gè)自定義的配置器(configurer)。這些配置器定義了特定的認(rèn)證方式或者處理特定類(lèi)型的認(rèn)證請(qǐng)求
我簡(jiǎn)單說(shuō)下:

  • .apply(new TokenAuthenticationConfigurer()): 這一行代碼聲明了一個(gè)名為 TokenAuthenticationConfigurer
    的配置器,這個(gè)配置器我實(shí)現(xiàn)了攔截基于令牌(JWT)的身份驗(yàn)證

  • .apply(new PhoneLoginConfigurer()): 這一行代碼聲明了一個(gè)名為 PhoneLoginConfigurer 的配置器,準(zhǔn)確的說(shuō)是手機(jī)號(hào)
    密碼登錄這個(gè)配置器用于配置手機(jī)登錄認(rèn)證方式,包含手機(jī)號(hào) + 密碼登錄

  • .apply(new WxQrLoginConfigurer()): 這一行代碼應(yīng)用了一個(gè)名為 WxQrLoginConfigurer 的配置器。根據(jù)名稱(chēng)應(yīng)該可以知道,是一個(gè)前端掃碼微信二維碼登錄,
    這個(gè)配置了微信掃碼登錄認(rèn)證方式,涉及對(duì)接微信授權(quán)。

這三個(gè)配置器的先后順序非常重要,因?yàn)樗鼈兛赡軙?huì)相互依賴(lài)或者有先后執(zhí)行的邏輯,說(shuō)這里的順序可能影響到程序的正確性。如果這些配置器之間有先后順序的要求,而沒(méi)有按照要求配置,就有可能出現(xiàn)空指針異常(NULL
異常)或其他配置錯(cuò)誤。在使用這些配置器時(shí)務(wù)必注意它們的先后順序

再就是微信掃碼注冊(cè)實(shí)現(xiàn) 還記得 我們前端調(diào)用的這個(gè)接口嗎?

@GetMapping("/qr/{mode}")

當(dāng)mode = register時(shí) 我會(huì)重定向到前端登錄頁(yè)

response.sendRedirect("https://www.xxxx.com/login/#/?uuid=" + code);

就是我會(huì)在注冊(cè)頁(yè)Login.vue created 鉤子函數(shù)里面
判斷 ?uuid=" + code 是否存在 存在的話(huà) 就說(shuō)明是用戶(hù)正在注冊(cè) 讓用戶(hù) 輸入 手機(jī)號(hào) 密碼 和驗(yàn)證碼注冊(cè)
注冊(cè)接口 如下

具體參數(shù),其實(shí)無(wú)非就是你注冊(cè)的手機(jī)號(hào) 還有密碼 這個(gè)根據(jù)你的業(yè)務(wù)去寫(xiě)
loginVo 實(shí)體類(lèi)包含uuid這個(gè)字段 這個(gè)u id的值其實(shí)就是微信掃碼登錄成功返回 code
** response.sendRedirect(“https://www.xxxx.com/login/#/?uuid=” + code);**
所以前端要解析出 uuid 并在注冊(cè)的時(shí)候把這個(gè)值傳過(guò)來(lái),獲取微信用戶(hù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-846032.html


    public ResultBean addUser(LoginVo loginVo) {
		// 手機(jī)驗(yàn)證碼是否為空
        if (StringUtils.isEmpty(loginVo.getCode())) {
            return ResultBean.error(ResultBeanEnum.CODE_NOTNULL_ERROR); // 驗(yàn)證碼為空
        }
		// 校驗(yàn)驗(yàn)證碼
        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>();
		// 判斷手機(jī)號(hào)是否注冊(cè)過(guò)
        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());
        // 判斷微信是否已經(jīng)綁定了其他手機(jī)號(hào)
        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) {
        // 注冊(cè)成功
            return ResultBean.success();
        }
        return ResultBean.error(ResultBeanEnum.USER_ADD_ERROR);
    }

到了這里,關(guān)于使用Spring Boot Security 實(shí)現(xiàn)多認(rèn)證 手機(jī)號(hào)登錄 微信掃碼登錄 微信掃碼注冊(cè)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • spring boot security使用jwt認(rèn)證

    spring boot security使用jwt認(rèn)證

    在前面的幾篇文章中: spring boot security快速使用示例 spring boot security之前后端分離配置 spring boot security自定義認(rèn)證 spring boot security驗(yàn)證碼登錄示例 基本對(duì)常用的基于cookie和session的認(rèn)證使用場(chǎng)景都已覆蓋。但是session屬于有狀態(tài)認(rèn)證,本文給出一個(gè)無(wú)狀態(tài)的認(rèn)證:jwt認(rèn)證示例。

    2024年02月12日
    瀏覽(17)
  • Spring Boot 如何使用 Spring Security 進(jìn)行認(rèn)證和授權(quán)

    Spring Boot 如何使用 Spring Security 進(jìn)行認(rèn)證和授權(quán)

    在 Web 應(yīng)用程序中,認(rèn)證和授權(quán)是非常重要的功能。Spring Security 是一個(gè)基于 Spring 框架的強(qiáng)大的安全框架,它提供了完整的認(rèn)證和授權(quán)解決方案,并且可以輕松地集成到 Spring Boot 應(yīng)用程序中。本文將介紹如何在 Spring Boot 中使用 Spring Security 進(jìn)行認(rèn)證和授權(quán),并提供示例代碼。

    2024年02月11日
    瀏覽(37)
  • uniapp實(shí)現(xiàn)手機(jī)號(hào)一鍵登錄功能

    uniapp實(shí)現(xiàn)手機(jī)號(hào)一鍵登錄功能

    1,第一步 2,第二步 創(chuàng)建應(yīng)用要和項(xiàng)目uni-appid一致。 3,第三步 4,第四步 5,第五步 6,第六步 7,第七步 8,第八步 (實(shí)現(xiàn)代碼) 一建登錄步驟到此結(jié)束,歡迎大家討論和指導(dǎo),登錄彈窗本文設(shè)置的是全屏‘fullScreen’,大家可根據(jù)需求編輯,彈窗只能在手機(jī)端app才能顯示。

    2024年02月16日
    瀏覽(24)
  • 手機(jī)號(hào)加解密業(yè)務(wù),通過(guò)aop實(shí)現(xiàn)

    序言: 在開(kāi)發(fā)過(guò)程中因?yàn)楣δ艿奶厥庑? 需要對(duì)客戶(hù)信息的手機(jī)號(hào)進(jìn)行加密處理 然后存到數(shù)據(jù)庫(kù)中,然而這個(gè)需求是項(xiàng)目中期加入,很多功能上已經(jīng)使用了獲取客戶(hù)信息的方法,沒(méi)法統(tǒng)一控制手機(jī)號(hào)加解密操作, 于是考慮使用 aop做環(huán)繞增強(qiáng) 對(duì)所有出參進(jìn)行,解密操作(這里只

    2024年02月11日
    瀏覽(25)
  • Java實(shí)現(xiàn)對(duì)手機(jī)號(hào)、身份證號(hào)、護(hù)照號(hào)脫敏

    背景: 我們?cè)陧?xiàng)目中經(jīng)常會(huì)需要用到用戶(hù)的敏感信息,比如手機(jī)號(hào)、身份證號(hào)、護(hù)照號(hào); 當(dāng)數(shù)據(jù)需要在頁(yè)面上進(jìn)行展示的時(shí)候就需要進(jìn)行脫敏,將其中幾位變?yōu)?*。 官方文檔: https://www.hutool.cn/docs/#/core/工具類(lèi)/信息脫敏工具-DesensitizedUtil Hutool依賴(lài): 代碼實(shí)現(xiàn): 執(zhí)行結(jié)果:

    2024年02月15日
    瀏覽(22)
  • 微信小程序: java實(shí)現(xiàn)獲取手機(jī)號(hào)方式

    目錄 1. 現(xiàn)在比較簡(jiǎn)單的方式 - 接口名 --- 功能描述 - 調(diào)用方式 --- HTTPS 調(diào)用 --- 第三方調(diào)用 --- 請(qǐng)求參數(shù) --- 返回參數(shù) 2. 實(shí)現(xiàn)方式 1. 加入fastjson依賴(lài)? 2. http請(qǐng)求類(lèi) 3. Json串工具類(lèi) 4.接口方法 3.另外介紹一點(diǎn)access_token 微信官方文檔介紹:? getPhoneNumber --- 功能描述 該接口需配合手機(jī)

    2024年02月16日
    瀏覽(24)
  • 怎么注冊(cè)Google賬號(hào)(使用國(guó)內(nèi)手機(jī)號(hào)注冊(cè))

    怎么注冊(cè)Google賬號(hào)(使用國(guó)內(nèi)手機(jī)號(hào)注冊(cè))

    記錄一下如何用 國(guó)內(nèi)的手機(jī)號(hào) 注冊(cè)Google賬號(hào) Google官網(wǎng)(沒(méi)有tizi是進(jìn)不去的)點(diǎn)擊登錄 如圖所示進(jìn)入創(chuàng)建賬號(hào)頁(yè)面 你會(huì)發(fā)現(xiàn),這樣操作的話(huà),國(guó)內(nèi)的手機(jī)號(hào)是不允許被使用的。 點(diǎn)擊瀏覽器右上角的 “三點(diǎn)” 選擇 “語(yǔ)言” ,我們要做的就是 將瀏覽器的顯示語(yǔ)言改為英文(

    2024年02月01日
    瀏覽(27)
  • 能否通過(guò)手機(jī)號(hào)查詢(xún)他人位置及技術(shù)實(shí)現(xiàn)(省流:不能)

    能否通過(guò)手機(jī)號(hào)查詢(xún)他人位置及技術(shù)實(shí)現(xiàn)(省流:不能)

    ??作者簡(jiǎn)介:被吉師散養(yǎng)、喜歡前端、學(xué)過(guò)后端、練過(guò)CTF、玩過(guò)DOS、不喜歡java的不知名學(xué)生。 ??個(gè)人主頁(yè):紅中 ??每日emo:紀(jì)念我死去的愛(ài)情 ??靈感來(lái)源:藝術(shù)源于生活,而高于生活 頭部聲明:如果您是來(lái)學(xué)怎么定位他人的,不好意思,這是違法行為,我不會(huì),也不會(huì)

    2024年02月02日
    瀏覽(21)
  • uniapp 實(shí)現(xiàn)微信小程序手機(jī)號(hào)一鍵登錄

    uniapp 實(shí)現(xiàn)微信小程序手機(jī)號(hào)一鍵登錄

    app 和 h5 手機(jī)號(hào)一鍵登錄,參考文檔:uni-app官網(wǎng) 以下是uniapp 實(shí)現(xiàn)微信小程序手機(jī)號(hào)一鍵登錄 1、布局

    2024年02月03日
    瀏覽(19)
  • vue項(xiàng)目表單使用正則過(guò)濾ip、手機(jī)號(hào)

    vue項(xiàng)目表單使用正則過(guò)濾ip、手機(jī)號(hào)

    useFormValidate .js 手機(jī)號(hào)驗(yàn)證

    2024年02月03日
    瀏覽(25)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包