电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Spring Security 實現短信驗證碼登錄功能

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

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

開發短信驗證碼接口

獲取驗證碼

短信驗證碼的發送獲取邏輯和圖片驗證碼類似,這里直接貼出代碼。

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

前端代碼

<tr><td>手機號:</td><td><input type='text' name='mobile' value='13012345678'></td></tr><tr><td>短信驗證碼:</td><td><input type='text' name='smsCode'><a href='http://www.hdgsjgj.cn/code/sms?mobile=13012345678' rel='external nofollow' >發送驗證碼</a></td></tr>

短信驗證碼流程原理

短信驗證碼登錄和用戶名密碼登錄對比

Spring Security 實現短信驗證碼登錄功能

步驟流程

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

代碼實現

SmsCodeAuthenticationToken

作用:封裝認證Token 實現:可以繼承AbstractAuthenticationToken抽象類,該類實現了Authentication接口

public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;private final Object principal;/** * 進入SmsAuthenticationFilter時,構建一個未認證的Token * * @param mobile */public SmsCodeAuthenticationToken(String mobile) {super(null);this.principal = mobile;setAuthenticated(false);}/** * 認證成功以后構建為已認證的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

作用:處理短信登錄的請求,構建Token,把請求信息設置到Token中。 實現:該類可以模仿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);// 把請求信息設到Token中setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}/** * 獲取手機號 */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

作用:提供認證Token的校驗邏輯,配置為能夠支持SmsCodeAuthenticationToken的校驗 實現:實現AuthenticationProvider接口,實現其兩個方法。

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;} /** * 表示支持校驗的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

:校驗短信驗證碼 實現:和圖形驗證碼類似,繼承OncePerRequestFilter接口防止多次調用,主要就是驗證碼驗證邏輯,驗證通過則繼續下一個過濾器。

@Component('validateCodeFilter')public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {/** * 驗證碼校驗失敗處理器 */@Autowiredprivate AuthenticationFailureHandler authenticationFailureHandler;/** * 系統配置信息 */@Autowiredprivate SecurityProperties securityProperties;/** * 系統中的校驗碼處理器 */@Autowiredprivate ValidateCodeProcessorHolder validateCodeProcessorHolder;/** * 存放所有需要校驗驗證碼的url */private Map<String, ValidateCodeType> urlMap = new HashMap<>();/** * 驗證請求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);}/** * 講系統中配置的需要校驗驗證碼的URL根據校驗的類型放入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);}}}/** * 驗證短信驗證碼 * * @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('校驗請求(' + request.getRequestURI() + ')中的驗證碼,驗證碼類型' + type);try {// 進行驗證碼的校驗validateCodeProcessorHolder.findValidateCodeProcessor(type).validate(new ServletWebRequest(request, response));logger.info('驗證碼校驗通過');} catch (ValidateCodeException exception) {// 如果校驗拋出異常,則交給我們之前文章定義的異常處理器進行處理authenticationFailureHandler.onAuthenticationFailure(request, response, exception);return;}}// 繼續調用后邊的過濾器chain.doFilter(request, response);}/** * 獲取校驗碼的類型,如果當前請求不需要校驗,則返回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();// 設置AuthenticationManagersmsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));// 設置登錄成功處理器smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(meicloudAuthenticationSuccessHandler);// 設置登錄失敗處理器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

作用:主配置類;添加短信驗證碼配置類、添加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 {// 驗證碼校驗過濾器ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();// 將驗證碼校驗過濾器加到 UsernamePasswordAuthenticationFilter 過濾器之前http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class).formLogin()// 當用戶登錄認證時默認跳轉的頁面.loginPage('/authentication/require')// 以下這行 UsernamePasswordAuthenticationFilter 會知道要處理表單的 /authentication/form 請求,而不是默認的 /login.loginProcessingUrl('/authentication/form').successHandler(meicloudAuthenticationSuccessHandler).failureHandler(meicloudAuthenticationFailureHandler)// 配置記住我功能.and().rememberMe()// 配置TokenRepository.tokenRepository(persistentTokenRepository())// 配置Token過期時間.tokenValiditySeconds(3600)// 最終拿到用戶名之后,使用UserDetailsService去做登錄.userDetailsService(userDetailsService).and().authorizeRequests()// 排除對 '/authentication/require' 和 '/meicloud-signIn.html' 的身份驗證.antMatchers('/authentication/require', securityProperties.getBrowser().getSignInPage(), '/code/*').permitAll()// 表示所有請求都需要身份驗證.anyRequest().authenticated().and().csrf().disable()// 暫時把跨站請求偽造的功能關閉掉// 相當于把smsCodeAuthenticationSecurityConfig里的配置加到上面這些配置的后面.apply(smsCodeAuthenticationSecurityConfig);}/** * 記住我功能的Token存取器配置 * * @return */@Beanpublic PersistentTokenRepository persistentTokenRepository() {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 啟動的時候自動創建表,建表語句 JdbcTokenRepositoryImpl 已經都寫好了tokenRepository.setCreateTableOnStartup(true);return tokenRepository;}}

總結

到此這篇關于Spring Security 實現短信驗證碼登錄功能的文章就介紹到這了,更多相關spring security 驗證碼登錄內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
主站蜘蛛池模板: 政府回应:200块在义乌小巷能买到爱情吗?——揭秘打工族省钱约会的生存智慧 | 仓储笼_仓储货架_南京货架_仓储货架厂家_南京货架价格低-南京一品仓储设备制造公司 | 【甲方装饰】合肥工装公司-合肥装修设计公司,专业从事安徽办公室、店面、售楼部、餐饮店、厂房装修设计服务 | 新型游乐设备,360大摆锤游乐设备「诚信厂家」-山东方鑫游乐设备 新能源汽车电池软连接,铜铝复合膜柔性连接,电力母排-容发智能科技(无锡)有限公司 | 盐城网络公司_盐城网站优化_盐城网站建设_盐城市启晨网络科技有限公司 | 超声波气象站_防爆气象站_空气质量监测站_负氧离子检测仪-风途物联网 | led全彩屏-室内|学校|展厅|p3|户外|会议室|圆柱|p2.5LED显示屏-LED显示屏价格-LED互动地砖屏_蕙宇屏科技 | 本安接线盒-本安电路用接线盒-本安分线盒-矿用电话接线盒-JHH生产厂家-宁波龙亿电子科技有限公司 | 杜康白酒加盟_杜康酒代理_杜康酒招商加盟官网_杜康酒厂加盟总代理—杜康酒神全国运营中心 | 「阿尔法设计官网」工业设计_产品设计_产品外观设计 深圳工业设计公司 | ISO9001认证咨询_iso9001企业认证代理机构_14001|18001|16949|50430认证-艾世欧认证网 | 袋式过滤器,自清洗过滤器,保安过滤器,篮式过滤器,气体过滤器,全自动过滤器,反冲洗过滤器,管道过滤器,无锡驰业环保科技有限公司 | 磁力轮,磁力联轴器,磁齿轮,钕铁硼磁铁-北京磁运达厂家 | 手机游戏_热门软件app下载_好玩的安卓游戏下载基地-吾爱下载站 | 阳光1号桔柚_无核沃柑_柑橘新品种枝条苗木批发 - 苧金网 | 不锈钢电动球阀_气动高压闸阀_旋塞疏水调节阀_全立阀门-来自温州工业阀门巨头企业 | 北京遮阳网-防尘盖土网-盖土草坪-迷彩网-防尘网生产厂家-京兴科技 | 阴离子_阳离子聚丙烯酰胺厂家_聚合氯化铝价格_水处理絮凝剂_巩义市江源净水材料有限公司 | 泉州陶瓷pc砖_园林景观砖厂家_石英砖地铺石价格 _福建暴风石英砖 | 断桥铝破碎机_发动机破碎机_杂铝破碎机厂家价格-皓星机械 | 礼至家居-全屋定制家具_一站式全屋整装_免费量房设计报价 | 算命免费_生辰八字_免费在线算命 - 卜算子算命网| 礼仪庆典公司,礼仪策划公司,庆典公司,演出公司,演艺公司,年会酒会,生日寿宴,动工仪式,开工仪式,奠基典礼,商务会议,竣工落成,乔迁揭牌,签约启动-东莞市开门红文化传媒有限公司 | 煤棒机_增碳剂颗粒机_活性炭颗粒机_木炭粉成型机-巩义市老城振华机械厂 | 长沙一级消防工程公司_智能化弱电_机电安装_亮化工程专业施工承包_湖南公共安全工程有限公司 | 北京印刷厂_北京印刷_北京印刷公司_北京印刷厂家_北京东爵盛世印刷有限公司 | 雨水收集系统厂家-雨水收集利用-模块雨水收集池-徐州博智环保科技有限公司 | HDPE土工膜,复合土工膜,防渗膜价格,土工膜厂家-山东新路通工程材料有限公司 | 真空包装机-诸城市坤泰食品机械有限公司 | 专业深孔加工_东莞深孔钻加工_东莞深孔钻_东莞深孔加工_模具深孔钻加工厂-东莞市超耀实业有限公司 | 变频器维修公司_plc维修_伺服驱动器维修_工控机维修 - 夫唯科技 变位机,焊接变位机,焊接变位器,小型变位机,小型焊接变位机-济南上弘机电设备有限公司 | 兰州UPS电源,兰州山特UPS-兰州万胜商贸| 美的商用净水器_美的直饮机_一级代理经销商_Midea租赁价格-厂家反渗透滤芯-直饮水批发品牌售后 | 智能型高压核相仪-自动开口闪点测试仪-QJ41A电雷管测试仪|上海妙定 | 西安中国国际旅行社(西安国旅) | 报警器_家用防盗报警器_烟雾报警器_燃气报警器_防盗报警系统厂家-深圳市刻锐智能科技有限公司 | 自动化展_机器人展_机床展_工业互联网展_广东佛山工博会 | 闪电优家-卫生间防水补漏_酒店漏水渗水维修_防水堵漏公司 | 除尘布袋_液体过滤袋_针刺毡滤料-杭州辉龙过滤技术有限公司 | 室内室外厚型|超薄型|非膨胀型钢结构防火涂料_隧道专用防火涂料厂家|电话|价格|批发|施工 | 挖掘机挖斗和铲斗生产厂家选择徐州崛起机械制造有限公司 |