亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

Spring Security 實(shí)現(xiàn)短信驗(yàn)證碼登錄功能

瀏覽:41日期:2023-09-05 09:49:10

之前文章都是基于用戶名密碼登錄,第六章圖形驗(yàn)證碼登錄其實(shí)還是用戶名密碼登錄,只不過多了一層圖形驗(yàn)證碼校驗(yàn)而已;Spring Security默認(rèn)提供的認(rèn)證流程就是用戶名密碼登錄,整個(gè)流程都已經(jīng)固定了,雖然提供了一些接口擴(kuò)展,但是有些時(shí)候我們就需要有自己特殊的身份認(rèn)證邏輯,比如用短信驗(yàn)證碼登錄,它和用戶名密碼登錄的邏輯是不一樣的,這時(shí)候就需要重新寫一套身份認(rèn)證邏輯。

開發(fā)短信驗(yàn)證碼接口

獲取驗(yàn)證碼

短信驗(yàn)證碼的發(fā)送獲取邏輯和圖片驗(yàn)證碼類似,這里直接貼出代碼。

@GetMapping('/code/sms')public void createSmsCode(HttpServletRequest request, HttpServletResponse response) throws Exception {// 創(chuàng)建驗(yàn)證碼ValidateCode smsCode = createCodeSmsCode(request);// 將驗(yàn)證碼放到session中sessionStrategy.setAttribute(new ServletWebRequest(request), SMS_CODE_SESSION_KEY, smsCode);String mobile = ServletRequestUtils.getRequiredStringParameter(request, 'mobile');// 發(fā)送驗(yàn)證碼smsCodeSender.send(mobile, smsCode.getCode());}

前端代碼

<tr><td>手機(jī)號(hào):</td><td><input type='text' name='mobile' value='13012345678'></td></tr><tr><td>短信驗(yàn)證碼:</td><td><input type='text' name='smsCode'><a href='http://www.aoyou183.cn/code/sms?mobile=13012345678' rel='external nofollow' >發(fā)送驗(yàn)證碼</a></td></tr>

短信驗(yàn)證碼流程原理

短信驗(yàn)證碼登錄和用戶名密碼登錄對(duì)比

Spring Security 實(shí)現(xiàn)短信驗(yàn)證碼登錄功能

步驟流程

首先點(diǎn)擊登錄應(yīng)該會(huì)被SmsAuthenticationFilter過濾器處理,這個(gè)過濾器拿到請求以后會(huì)在登錄請求中拿到手機(jī)號(hào),然后封裝成自定義的一個(gè)SmsAuthenticationToken(未認(rèn)證)。 這個(gè)Token也會(huì)傳給AuthenticationManager,因?yàn)锳uthenticationManager整個(gè)系統(tǒng)只有一個(gè),它會(huì)檢索系統(tǒng)中所有的AuthenticationProvider,這時(shí)候我們要提供自己的SmsAuthenticationProvider,用它來校驗(yàn)自己寫的SmsAuthenticationToken的手機(jī)號(hào)信息。 在校驗(yàn)的過程中同樣會(huì)調(diào)用UserDetailsService,把手機(jī)號(hào)傳給它讓它去讀用戶信息,去判斷是否能登錄,登錄成功的話再把SmsAuthenticationToken標(biāo)記為已認(rèn)證。 到這里為止就是短信驗(yàn)證碼的認(rèn)證流程,上面的流程并沒有提到校驗(yàn)驗(yàn)證碼信息,其實(shí)它的驗(yàn)證流程和圖形驗(yàn)證碼驗(yàn)證流程也是類似,同樣是在SmsAuthenticationFilter過濾器之前加一個(gè)過濾器來驗(yàn)證短信驗(yàn)證碼。

代碼實(shí)現(xiàn)

SmsCodeAuthenticationToken

作用:封裝認(rèn)證Token 實(shí)現(xiàn):可以繼承AbstractAuthenticationToken抽象類,該類實(shí)現(xiàn)了Authentication接口

public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;private final Object principal;/** * 進(jìn)入SmsAuthenticationFilter時(shí),構(gòu)建一個(gè)未認(rèn)證的Token * * @param mobile */public SmsCodeAuthenticationToken(String mobile) {super(null);this.principal = mobile;setAuthenticated(false);}/** * 認(rèn)證成功以后構(gòu)建為已認(rèn)證的Token * * @param principal * @param authorities */public SmsCodeAuthenticationToken(Object principal,Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;super.setAuthenticated(true);}@Overridepublic Object getCredentials() {return null;}@Overridepublic Object getPrincipal() {return this.principal;}@Overridepublic 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();}}

SmsCodeAuthenticationFilter

作用:處理短信登錄的請求,構(gòu)建Token,把請求信息設(shè)置到Token中。 實(shí)現(xiàn):該類可以模仿UsernamePasswordAuthenticationFilter類,繼承AbstractAuthenticationProcessingFilter抽象類

public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private String mobileParameter = 'mobile';private boolean postOnly = true; /** * 表示要處理的請求路徑 */public SmsCodeAuthenticationFilter() { super(new AntPathRequestMatcher('/authentication/mobile', 'POST'));} @Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {if (postOnly && !request.getMethod().equals('POST')) {throw new AuthenticationServiceException('Authentication method not supported: ' + request.getMethod());}String mobile = obtainMobile(request);if (mobile == null) {mobile = '';}mobile = mobile.trim();SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);// 把請求信息設(shè)到Token中setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}/** * 獲取手機(jī)號(hào) */protected String obtainMobile(HttpServletRequest request) {return request.getParameter(mobileParameter);}protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {authRequest.setDetails(authenticationDetailsSource.buildDetails(request));}public void setMobileParameter(String usernameParameter) {Assert.hasText(usernameParameter, 'Username parameter must not be empty or null');this.mobileParameter = usernameParameter;}public void setPostOnly(boolean postOnly) {this.postOnly = postOnly;}public final String getMobileParameter() {return mobileParameter;}}

SmsAuthenticationProvider

作用:提供認(rèn)證Token的校驗(yàn)邏輯,配置為能夠支持SmsCodeAuthenticationToken的校驗(yàn) 實(shí)現(xiàn):實(shí)現(xiàn)AuthenticationProvider接口,實(shí)現(xiàn)其兩個(gè)方法。

public class SmsCodeAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userDetailsService; /** * 進(jìn)行身份認(rèn)證的邏輯 * * @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;} /** * 表示支持校驗(yàn)的Token,這里是SmsCodeAuthenticationToken * * @param authentication * @return */@Overridepublic boolean supports(Class<?> authentication) {return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);}public UserDetailsService getUserDetailsService() {return userDetailsService;}public void setUserDetailsService(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}}

ValidateCodeFilter

:校驗(yàn)短信驗(yàn)證碼 實(shí)現(xiàn):和圖形驗(yàn)證碼類似,繼承OncePerRequestFilter接口防止多次調(diào)用,主要就是驗(yàn)證碼驗(yàn)證邏輯,驗(yàn)證通過則繼續(xù)下一個(gè)過濾器。

@Component('validateCodeFilter')public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {/** * 驗(yàn)證碼校驗(yàn)失敗處理器 */@Autowiredprivate AuthenticationFailureHandler authenticationFailureHandler;/** * 系統(tǒng)配置信息 */@Autowiredprivate SecurityProperties securityProperties;/** * 系統(tǒng)中的校驗(yàn)碼處理器 */@Autowiredprivate ValidateCodeProcessorHolder validateCodeProcessorHolder;/** * 存放所有需要校驗(yàn)驗(yàn)證碼的url */private Map<String, ValidateCodeType> urlMap = new HashMap<>();/** * 驗(yàn)證請求url與配置的url是否匹配的工具類 */private AntPathMatcher pathMatcher = new AntPathMatcher();/** * 初始化要攔截的url配置信息 */@Overridepublic void afterPropertiesSet() throws ServletException {super.afterPropertiesSet();urlMap.put('/authentication/mobile', ValidateCodeType.SMS);addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);}/** * 講系統(tǒng)中配置的需要校驗(yàn)驗(yàn)證碼的URL根據(jù)校驗(yàn)的類型放入map * * @param urlString * @param type */protected void addUrlToMap(String urlString, ValidateCodeType type) {if (StringUtils.isNotBlank(urlString)) {String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlString, ',');for (String url : urls) {urlMap.put(url, type);}}}/** * 驗(yàn)證短信驗(yàn)證碼 * * @param request * @param response * @param chain * @throws ServletException * @throws IOException */@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {ValidateCodeType type = getValidateCodeType(request);if (type != null) {logger.info('校驗(yàn)請求(' + request.getRequestURI() + ')中的驗(yàn)證碼,驗(yàn)證碼類型' + type);try {// 進(jìn)行驗(yàn)證碼的校驗(yàn)validateCodeProcessorHolder.findValidateCodeProcessor(type).validate(new ServletWebRequest(request, response));logger.info('驗(yàn)證碼校驗(yàn)通過');} catch (ValidateCodeException exception) {// 如果校驗(yàn)拋出異常,則交給我們之前文章定義的異常處理器進(jìn)行處理authenticationFailureHandler.onAuthenticationFailure(request, response, exception);return;}}// 繼續(xù)調(diào)用后邊的過濾器chain.doFilter(request, response);}/** * 獲取校驗(yàn)碼的類型,如果當(dāng)前請求不需要校驗(yàn),則返回null * * @param request * @return */private ValidateCodeType getValidateCodeType(HttpServletRequest request) {ValidateCodeType result = null;if (!StringUtils.equalsIgnoreCase(request.getMethod(), 'GET')) {Set<String> urls = urlMap.keySet();for (String url : urls) {if (pathMatcher.match(url, request.getRequestURI())) {result = urlMap.get(url);}}}return result;}}

添加配置

SmsCodeAuthenticationSecurityConfig

作用:配置SmsCodeAuthenticationFilter,后面需要把這些配置加到主配置類BrowserSecurityConfig

@Componentpublic class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {@Autowiredprivate AuthenticationSuccessHandler meicloudAuthenticationSuccessHandler;@Autowiredprivate AuthenticationFailureHandler meicloudAuthenticationFailureHandler;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate PersistentTokenRepository persistentTokenRepository;@Overridepublic void configure(HttpSecurity http) throws Exception {SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();// 設(shè)置AuthenticationManagersmsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));// 設(shè)置登錄成功處理器smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(meicloudAuthenticationSuccessHandler);// 設(shè)置登錄失敗處理器smsCodeAuthenticationFilter.setAuthenticationFailureHandler(meicloudAuthenticationFailureHandler);String key = UUID.randomUUID().toString();smsCodeAuthenticationFilter.setRememberMeServices(new PersistentTokenBasedRememberMeServices(key, userDetailsService, persistentTokenRepository));SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);// 將自己寫的Provider加到Provider集合里去http.authenticationProvider(smsCodeAuthenticationProvider).addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);}}

BrowserSecurityConfig

作用:主配置類;添加短信驗(yàn)證碼配置類、添加SmsCodeAuthenticationSecurityConfig配置

@Configurationpublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Autowiredprivate SecurityProperties securityProperties;@Autowiredprivate DataSource dataSource;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate AuthenticationSuccessHandler meicloudAuthenticationSuccessHandler;@Autowiredprivate AuthenticationFailureHandler meicloudAuthenticationFailureHandler;@Autowiredprivate SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;@Overrideprotected void configure(HttpSecurity http) throws Exception {// 驗(yàn)證碼校驗(yàn)過濾器ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();// 將驗(yàn)證碼校驗(yàn)過濾器加到 UsernamePasswordAuthenticationFilter 過濾器之前http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class).formLogin()// 當(dāng)用戶登錄認(rèn)證時(shí)默認(rèn)跳轉(zhuǎn)的頁面.loginPage('/authentication/require')// 以下這行 UsernamePasswordAuthenticationFilter 會(huì)知道要處理表單的 /authentication/form 請求,而不是默認(rèn)的 /login.loginProcessingUrl('/authentication/form').successHandler(meicloudAuthenticationSuccessHandler).failureHandler(meicloudAuthenticationFailureHandler)// 配置記住我功能.and().rememberMe()// 配置TokenRepository.tokenRepository(persistentTokenRepository())// 配置Token過期時(shí)間.tokenValiditySeconds(3600)// 最終拿到用戶名之后,使用UserDetailsService去做登錄.userDetailsService(userDetailsService).and().authorizeRequests()// 排除對(duì) '/authentication/require' 和 '/meicloud-signIn.html' 的身份驗(yàn)證.antMatchers('/authentication/require', securityProperties.getBrowser().getSignInPage(), '/code/*').permitAll()// 表示所有請求都需要身份驗(yàn)證.anyRequest().authenticated().and().csrf().disable()// 暫時(shí)把跨站請求偽造的功能關(guān)閉掉// 相當(dāng)于把smsCodeAuthenticationSecurityConfig里的配置加到上面這些配置的后面.apply(smsCodeAuthenticationSecurityConfig);}/** * 記住我功能的Token存取器配置 * * @return */@Beanpublic PersistentTokenRepository persistentTokenRepository() {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 啟動(dòng)的時(shí)候自動(dòng)創(chuàng)建表,建表語句 JdbcTokenRepositoryImpl 已經(jīng)都寫好了tokenRepository.setCreateTableOnStartup(true);return tokenRepository;}}

總結(jié)

到此這篇關(guān)于Spring Security 實(shí)現(xiàn)短信驗(yàn)證碼登錄功能的文章就介紹到這了,更多相關(guān)spring security 驗(yàn)證碼登錄內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 激情在线观看视频免费的 | 久久久久久久国产视频 | 小毛片在线观看 | 国产永久免费高清在线观看视频 | 欧美视频 亚洲视频 | 在线免费观看色片 | qvod高清在线成人观看 | 1769国内精品免费视频视频 | 亚洲综合图色40p | 国产一区二区日韩欧美在线 | 91精品麻豆 | 亚洲国产人久久久成人精品网站 | 一a级毛片 | 不卡在线观看 | 爱爱黄色| 天天色综合2 | 精品久久国产老人久久综合 | 国产精品一级二级三级 | 亚洲v欧美| 亚洲国产成人在线 | 免费一级欧美大片视频在线 | 婷婷成人综合 | 最新国产中文字幕 | 性黄色片 | 久久99爰这里有精品国产 | 美女的下部隐私网站99 | 色婷婷婷丁香亚洲综合不卡 | 高清中文字幕视频在线播 | 大色香蕉色视频大全 | 做a视频大全 | 丁香综合在线 | 亚洲欧美激情小说另类 | 丝袜无码一区二区三区 | 成熟女性毛茸茸撒尿厕所 | 黄网在线免费看 | 尤物蜜芽福利国产污在线观看 | 亚洲精品一区二区观看 | 午夜精品一区二区三区免费视频 | 高清成年美女xx免费网站黄 | 欧美成人免费在线视频 | 免费看的黄色小视频 |