javascript
SpringBoot + SpringSecurity 短信验证码登录功能实现
實現原理
在之前的文章中,我們介紹了普通的帳號密碼登錄的方式:SpringBoot + Spring Security 基本使用及個性化登錄配置(http://www.deiniu.com/article/140429.htm)。但是現在還有一種常見的方式,就是直接通過手機短信驗證碼登錄,這里就需要自己來做一些額外的工作了。
對 SpringSecurity 認證流程詳解http://www.deiniu.com/article/140800.htm有一定了解的都知道,在帳號密碼認證的過程中,涉及到了以下幾個類:UsernamePasswordAuthenticationFilter(用于請求參數獲取),UsernamePasswordAuthenticationToken(表示用戶登錄信息),ProviderManager(進行認證校驗),
因為是通過的短信驗證碼登錄,所以我們需要對請求的參數,認證過程,用戶登錄 Token 信息進行一定的重寫。當然驗證碼的過程我們應該放在最前面,如果圖形驗證碼的實現一樣。這樣的做法的好處是:將驗證碼認證該過程解耦出來,讓其他接口也可以使用到。
基本實現
驗證碼校驗
短信驗證碼的功能實現,其實和圖形驗證碼的原理是一樣的。只不過一個是返回給前端一個圖片,一個是給用戶發送短消息,這里只需要去調用一下短信服務商的接口就好了。更多的原理可以參考 SpringBoot + SpringSecurity 實現圖形驗證碼功能
AuthenticationToken
在使用帳號密碼登錄的時候,UsernamePasswordAuthenticationToken 里面包含了用戶的帳號,密碼,以及其他的是否可用等狀態信息。我們是通過手機短信來做登錄,所以就沒有密碼了,這里我們就直接將 UsernamePasswordAuthenticationToken 的代碼 copy 過來,把密碼相關的信息去掉就可以了
public?class?SmsCodeAuthenticationToken?extends?AbstractAuthenticationToken?{private?static?final?long?serialVersionUID?=?SpringSecurityCoreVersion.SERIAL_VERSION_UID;private?final?Object?principal;public?SmsCodeAuthenticationToken(String?mobile)?{super(null);this.principal?=?mobile;setAuthenticated(false);}public?SmsCodeAuthenticationToken(Object?principal,Collection<??extends?GrantedAuthority>?authorities)?{super(authorities);this.principal?=?principal;super.setAuthenticated(true);?//?must?use?super,?as?we?override}public?Object?getCredentials()?{return?null;}public?Object?getPrincipal()?{return?this.principal;}public?void?setAuthenticated(boolean?isAuthenticated)?throws?IllegalArgumentException?{if?(isAuthenticated)?{throw?new?IllegalArgumentException("Cannot?set?this?token?to?trusted?-?use?constructor?which?takes?a?GrantedAuthority?list?instead");}super.setAuthenticated(false);}@Overridepublic?void?eraseCredentials()?{super.eraseCredentials();} }AuthenticationFilter
在帳戶密碼登錄的流程中,默認使用的是 UsernamePasswordAuthenticationFilter,它的作用是從請求中獲取帳戶、密碼,請求方式校驗,生成 AuthenticationToken。這里我們的參數是有一定改變的,所以還是老方法,copy 過來進行簡單的修改
public?class?SmsCodeAuthenticationFilter?extends?AbstractAuthenticationProcessingFilter?{//?請求參數keyprivate?String?mobileParameter?=?SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE;//?是否只支持POSTprivate?boolean?postOnly?=?true;public?SmsCodeAuthenticationFilter()?{//?請求接口的urlsuper(new?AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,?"POST"));}public?Authentication?attemptAuthentication(HttpServletRequest?request,?HttpServletResponse?response)throws?AuthenticationException?{if?(postOnly?&&?!request.getMethod().equals("POST"))?{throw?new?AuthenticationServiceException("Authentication?method?not?supported:?"?+?request.getMethod());}//?根據請求參數名,獲取請求valueString?mobile?=?obtainMobile(request);if?(mobile?==?null)?{mobile?=?"";}mobile?=?mobile.trim();//?生成對應的AuthenticationTokenSmsCodeAuthenticationToken?authRequest?=?new?SmsCodeAuthenticationToken(mobile);setDetails(request,?authRequest);return?this.getAuthenticationManager().authenticate(authRequest);}/*?獲取手機號*/protected?String?obtainMobile(HttpServletRequest?request)?{return?request.getParameter(mobileParameter);}//?省略不相關代碼 }Provider
在帳號密碼登錄的過程中,密碼的正確性以及帳號是否可用是通過 DaoAuthenticationProvider 來校驗的。我們也應該自己實現一個 Provier
public?class?SmsCodeAuthenticationProvider?implements?AuthenticationProvider?{private?UserDetailsService?userDetailsService;/*?身份邏輯驗證*?@param?authentication*?@return*?@throws?AuthenticationException*/@Overridepublic?Authentication?authenticate(Authentication?authentication)?throws?AuthenticationException?{SmsCodeAuthenticationToken?authenticationToken?=?(SmsCodeAuthenticationToken)?authentication;UserDetails?user?=?userDetailsService.loadUserByUsername((String)?authenticationToken.getPrincipal());if?(user?==?null)?{throw?new?InternalAuthenticationServiceException("無法獲取用戶信息");}SmsCodeAuthenticationToken?authenticationResult?=?new?SmsCodeAuthenticationToken(user,?user.getAuthorities());authenticationResult.setDetails(authenticationToken.getDetails());return?authenticationResult;}@Overridepublic?boolean?supports(Class<?>?authentication)?{return?SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);}public?UserDetailsService?getUserDetailsService()?{return?userDetailsService;}public?void?setUserDetailsService(UserDetailsService?userDetailsService)?{this.userDetailsService?=?userDetailsService;} }配置
主要的認證流程就是通過以上四個過程實現的, 這里我們再降它們配置一下就可以了
@Component public?class?SmsCodeAuthenticationSecurityConfig?extends?SecurityConfigurerAdapter<DefaultSecurityFilterChain,?HttpSecurity>?{@Autowiredprivate?AuthenticationSuccessHandler?myAuthenticationSuccessHandler;@Autowiredprivate?AuthenticationFailureHandler?myAuthenticationFailureHandler;@Autowiredprivate?UserDetailsService?userDetailsService;@Overridepublic?void?configure(HttpSecurity?http)?throws?Exception?{SmsCodeAuthenticationFilter?smsCodeAuthenticationFilter?=?new?SmsCodeAuthenticationFilter();smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);SmsCodeAuthenticationProvider?smsCodeAuthenticationProvider?=?new?SmsCodeAuthenticationProvider();smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);http.authenticationProvider(smsCodeAuthenticationProvider).addFilterAfter(smsCodeAuthenticationFilter,?UsernamePasswordAuthenticationFilter.class);} }//?BrowerSecurityConfig.java @Override protected?void?configure(HttpSecurity?http)?throws?Exception?{http.apply(smsCodeAuthenticationSecurityConfig); }代碼下載
Spring-Security
“https://github.com/whyalwaysmea/Spring-Security
”來源鏈接:
http://www.deiniu.com/article/141972.htm
總結
以上是生活随笔為你收集整理的SpringBoot + SpringSecurity 短信验证码登录功能实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何利用solidworks、CAD对平
- 下一篇: 数据库语句