SpringSecurity 手機(jī)號(hào)登錄功能實(shí)現(xiàn)
一、工作流程
1.向手機(jī)發(fā)送驗(yàn)證碼,第三方短信發(fā)送平臺(tái),如阿里云短信。
2.手機(jī)獲取驗(yàn)證碼后,在表單中輸入驗(yàn)證碼。
3.使用自定義過(guò)濾器?SmsCodeValidateFilter?。
4.短信校驗(yàn)通過(guò)后,使用自定義手機(jī)認(rèn)證過(guò)濾器?SmsCodeAuthenticationFilter校驗(yàn)手機(jī)號(hào)碼是否存在?。
5.自定義?SmsCodeAuthenticationToken?提供給?SmsCodeAuthenticationFilter?。
6.自定義?SmsCodeAuthenticationProvider?提供給?AuthenticationManager?。
7.創(chuàng)建針對(duì)手機(jī)號(hào)查詢(xún)用戶(hù)信息的?SmsCodeUserDetailsService?,提交給?。SmsCodeAuthenticationProvider?。
8.自定義?SmsCodeSecurityConfig?配置類(lèi)將上面組件連接起來(lái)。
9.將?SmsCodeSecurityConfig?添加到?LearnSrpingSecurity?安全配置的過(guò)濾器鏈上。
二、實(shí)現(xiàn)
2.1、驗(yàn)證碼生成、發(fā)送
/** * 創(chuàng)建驗(yàn)證碼生成器 */ @Component public class SmsCodeGenerator { public String generate() { return RandomStringUtils.randomNumeric(4); } } /** * 驗(yàn)證碼發(fā)送器 */ @Component public class SmsCodeSender { public void send(String mobile, String code) { System.out.println("向手機(jī)" + mobile + "發(fā)送短信驗(yàn)證碼" + code); } } /** * 發(fā)送短信接口 */ @RestController public class ValidateCodeController { @Autowired private SmsCodeGenerator smsCodeGenerator; @Resource private SmsCodeSender smsCodeSender; @Resource private RedisTemplate redisTemplate; @GetMapping("/code/sms") public String createSmsCode(@RequestParam String mobile) throws IOException { //獲取驗(yàn)證碼 String smsCode = smsCodeGenerator.generate(); //把驗(yàn)證碼設(shè)置到redis redisTemplate.boundValueOps(SecurityConstants.getValidCodeKey(mobile)).set(smsCode, 300, TimeUnit.SECONDS); smsCodeSender.send("18360903475", "登錄驗(yàn)證碼為:" + smsCode + ",五分鐘過(guò)期"); return "驗(yàn)證碼是 : " + smsCode; } }
2.2、手機(jī)號(hào)碼認(rèn)證 Token
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityCoreVersion; import java.util.Collection; /** * 手機(jī)號(hào)碼認(rèn)證 Token */ public class PhoneNumAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; /** * principal的作用有兩個(gè), 在未登錄之前是用戶(hù)名,那么在登錄之后是用戶(hù)的信息。 */ private final Object principal; /** * 構(gòu)造 * @param principal 手機(jī)號(hào)碼 */ public PhoneNumAuthenticationToken(Object principal) { super(null); this.principal = principal; // 用于指示AbstractSecurityInterceptor是否應(yīng)向AuthenticationManager提供身份驗(yàn)證令牌 setAuthenticated(false); } /** * 構(gòu)造 * @param principal 用戶(hù)信息 * @param authorities 用戶(hù)權(quán)限列表 */ public PhoneNumAuthenticationToken(Object principal,Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; // 用于指示AbstractSecurityInterceptor是否應(yīng)向AuthenticationManager提供身份驗(yàn)證令牌 setAuthenticated(true); } /** * 正常這個(gè)是返回密碼,但手機(jī)登錄沒(méi)有密碼,不用管 */ @Override public Object getCredentials() { return null; } /** * 獲取手機(jī)號(hào)或用戶(hù)信息 */ @Override public Object getPrincipal() { return this.principal; } }
2.3、攔截請(qǐng)求、獲取手機(jī)號(hào)碼
/** * 手機(jī)號(hào)碼攔截器, 獲取手機(jī)號(hào)碼 */ public class PhoneNumAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public PhoneNumAuthenticationFilter() { super(new AntPathRequestMatcher("/phoneLogin", "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!Objects.equals(request.getMethod(),"POST")) { throw new AuthenticationServiceException("身份驗(yàn)證方法需為:'POST'請(qǐng)求"); } // 獲取手機(jī)號(hào) String phoneNum = Optional.ofNullable(request.getParameter(Constants.PHONE_NUM_PARAMETER)).map(String::trim).orElse(""); // new 手機(jī)號(hào)碼驗(yàn)證Token PhoneNumAuthenticationToken authRequest = new PhoneNumAuthenticationToken(phoneNum); // 身份驗(yàn)證詳細(xì)信息 authRequest.setDetails(super.authenticationDetailsSource.buildDetails(request)); return this.getAuthenticationManager().authenticate(authRequest); } }
2.4、短信驗(yàn)證碼驗(yàn)證過(guò)濾器
/** * 短信驗(yàn)證碼驗(yàn)證過(guò)濾器 */ @Component public class SmsCodeFilter extends OncePerRequestFilter { @Resource private StringRedisTemplate stringRedisTemplate; @Resource private CustomizeAuthencationFailureHandler customizeAuthencationFailureHandler; @Override protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException { /** * uri = /phoneLogin 即手機(jī)號(hào)碼登錄才攔截 */ if (Objects.equals(Constants.SMS_LOGIN_URI,request.getRequestURI())) { try{ // 驗(yàn)證手機(jī)驗(yàn)證碼 validateProcess(request); }catch (AuthenticationException ex) { customizeAuthencationFailureHandler.onAuthenticationFailure(request, response, ex); return; } } filterChain.doFilter(request, response); } /** * 驗(yàn)證手機(jī)驗(yàn)證碼 */ private void validateProcess(HttpServletRequest request){ // 獲取手機(jī)號(hào) String msgCode = stringRedisTemplate.opsForValue().get(Constants.SMS_CODE_SESSION_KEY); String code = request.getParameter(Constants.MSG_CODE); if(Strings.isBlank(code)) { throw new InternalAuthenticationServiceException("短信驗(yàn)證碼不能為空."); } if(null == msgCode) { throw new InternalAuthenticationServiceException("短信驗(yàn)證碼已失效."); } if(!code.equals(msgCode)) { throw new InternalAuthenticationServiceException("短信驗(yàn)證碼錯(cuò)誤."); } } }
2.5、繼承 WebSecurityConfigurerAdapter 配置 HttpSecurity
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 數(shù)據(jù)源 */ @Resource private DataSource dataSource; /** * 用戶(hù)信息服務(wù) */ @Resource private UserAuthentication userAuthentication; /** * 成功處理 */ @Resource private CustomizeAuthencationSuccessHandler customizeAuthencationSuccessHandler; /** * 失敗處理 */ @Resource private CustomizeAuthencationFailureHandler customizeAuthencationFailureHandler; /** * 用戶(hù)登出處理 */ @Resource private UserLogoutSuccessHandler userLogoutSuccessHandler; /** * 多用戶(hù)登錄處理 */ @Resource private MutilpleSessionHandler mutilpleSessionHandler; /** * 密碼編碼器 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 手機(jī)號(hào)碼登錄驗(yàn)證處理 */ @Resource private DaoPhoneNumAuthenticationProvider daoPhoneNumAuthenticationProvider; /** * 信息驗(yàn)證碼過(guò)濾器 */ @Resource private SmsCodeFilter smsCodeFilter; /** * 把AuthenticationManager公開(kāi) */ @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * 配置自定義驗(yàn)證查詢(xún)/加密工具 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userAuthentication).passwordEncoder(passwordEncoder()); } /** * 手機(jī)號(hào)碼登錄攔截器 */ @Bean public PhoneNumAuthenticationFilter phoneNumAuthenticationFilter() throws Exception { // 手機(jī)號(hào)碼攔截器, 獲取手機(jī)號(hào)碼 PhoneNumAuthenticationFilter phoneNumAuthenticationFilter = new PhoneNumAuthenticationFilter(); phoneNumAuthenticationFilter.setAuthenticationManager(authenticationManagerBean()); //使用手機(jī)號(hào)登錄失敗了如何處理 phoneNumAuthenticationFilter.setAuthenticationFailureHandler(customizeAuthencationFailureHandler); // 使用手機(jī)號(hào)登錄成功了如何處理 phoneNumAuthenticationFilter.setAuthenticationSuccessHandler(customizeAuthencationSuccessHandler); return phoneNumAuthenticationFilter; } @Override protected void configure(HttpSecurity http) throws Exception { http // 加入短信驗(yàn)證碼過(guò)濾器 .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 加入手機(jī)號(hào)碼登錄過(guò)濾器 .addFilterAfter(phoneNumAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) // 加入手機(jī)號(hào)碼登錄驗(yàn)證提供者 .authenticationProvider(daoPhoneNumAuthenticationProvider) // 表單登錄 .formLogin() // 未登錄跳轉(zhuǎn)登錄頁(yè)面 .loginPage("/login.html") // 指定登錄路徑 .loginProcessingUrl("/login") // 用戶(hù)登錄成功的處理 .successHandler(customizeAuthencationSuccessHandler) // 用戶(hù)登錄失敗的處理 .failureHandler(customizeAuthencationFailureHandler) // 因?yàn)橛脩?hù)傳入過(guò)來(lái)的token, 需要再次進(jìn)行校驗(yàn) .userDetailsService(userAuthentication) .tokenValiditySeconds(3600) // .alwaysRemember(true) // 認(rèn)證配置 .and() .authorizeRequests() //不攔截的Url .antMatchers("/login.html", "/image/code", "/smsCode", "/css/**", "/js/**", "/phoneLogin").permitAll() .anyRequest() //所有的請(qǐng)求 .authenticated() //認(rèn)證之后就可以訪問(wèn) // 多端登錄限制,限制一個(gè)賬號(hào)同時(shí)只能一個(gè)人登錄 .and() .sessionManagement() .maximumSessions(1) .expiredSessionStrategy(mutilpleSessionHandler) .and() // 登出配置 .and() .logout() .logoutUrl("/logout") // 登出成功處理 .logoutSuccessHandler(userLogoutSuccessHandler) .and() .csrf().disable(); } }
到此這篇關(guān)于SpringSecurity 手機(jī)號(hào)登錄的文章就介紹到這了,更多相關(guān)SpringSecurity 手機(jī)號(hào)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringSecurity整合springBoot、redis實(shí)現(xiàn)登錄互踢功能
- SpringSecurity構(gòu)建基于JWT的登錄認(rèn)證實(shí)現(xiàn)
- springboot+jwt+springSecurity微信小程序授權(quán)登錄問(wèn)題
- 詳解SpringSecurity中的Authentication信息與登錄流程
- SpringBoot整合SpringSecurity和JWT的示例
- SpringSecurity 默認(rèn)表單登錄頁(yè)展示流程源碼
- SpringSecurity動(dòng)態(tài)加載用戶(hù)角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能
- 解析SpringSecurity自定義登錄驗(yàn)證成功與失敗的結(jié)果處理問(wèn)題
相關(guān)文章
Java中對(duì)象的深復(fù)制(深克?。┖蜏\復(fù)制(淺克?。┙榻B
這篇文章主要介紹了Java中對(duì)象的深復(fù)制(深克?。┖蜏\復(fù)制(淺克隆) ,需要的朋友可以參考下2015-03-03SpringCloud消息總線Bus配置中心實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了SpringCloud消息總線Bus配置中心實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03關(guān)于springboot2整合lettuce啟動(dòng)卡住問(wèn)題的解決方法
Lettuce和Jedis的都是連接Redis Server的客戶(hù)端程序,下面這篇文章主要給大家介紹了關(guān)于springboot2整合lettuce啟動(dòng)卡住問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-12-12SpringBoot依賴(lài)和代碼分開(kāi)打包的實(shí)現(xiàn)步驟
本文主要介紹了SpringBoot依賴(lài)和代碼分開(kāi)打包的實(shí)現(xiàn)步驟,,這種方法將依賴(lài)和代碼分開(kāi)打包,一般更新只有代碼修改,Pom文件是不會(huì)經(jīng)常改動(dòng)的,感興趣的可以了解一下2023-10-10springboot中使用過(guò)濾器,jsoup過(guò)濾XSS腳本詳解
這篇文章主要介紹了springboot中使用過(guò)濾器,jsoup過(guò)濾XSS腳本詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12[Spring MVC]-詳解SpringMVC的各種參數(shù)綁定方式
本篇文章主要介紹了SpringMVC的各種參數(shù)綁定方式 ,具有一定的參考價(jià)值,有需要的可以了解一下。2016-12-12使用Spring?Boot+gRPC構(gòu)建微服務(wù)并部署的案例詳解
這篇文章主要介紹了使用Spring?Boot+gRPC構(gòu)建微服務(wù)并部署,Spring Cloud僅僅是一個(gè)開(kāi)發(fā)框架,沒(méi)有實(shí)現(xiàn)微服務(wù)所必須的服務(wù)調(diào)度、資源分配等功能,這些需求要借助Kubernetes等平臺(tái)來(lái)完成,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-06-06SQLSyntaxErrorException-ExecutorException報(bào)錯(cuò)解決分析
這篇文章主要為大家介紹了SQLSyntaxErrorException-ExecutorException報(bào)錯(cuò)解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08java虛擬機(jī)內(nèi)存溢出及泄漏實(shí)例
本篇文章給大家分享了java虛擬機(jī)內(nèi)存溢出及泄漏的實(shí)例以及相關(guān)知識(shí)點(diǎn)分享,有興趣的朋友參考學(xué)習(xí)下。2018-06-06