文章目录

  • 前言
  • spring-security认证流程
  • 自定义短信验证码登录
  • 总结

前言

前面几篇博客完成了验证码的重构和说明,这一篇开始介绍自定义的短信验证码登录。

spring-security认证流程

之前我们简单实现过一个用户名密码的表单登录——登录的请求会交给UsernamePasswordAuthenticationFilter这个过滤器,这个过滤器会组装一个UsernamePasswordAuthenticationToken的对象交给AuthenticationManagerAuthenticationManager这个对象在整个spring-security容器的容器中只有一个,之后AuthenticationManager会在所有的AuthenticationProvider中挑出支持当前认证请求Token的Provider实例,利用选中的Provider实例进行认证。在Provider认证的过程中,可以调用UserDetailsService进行用户认证。认证完成之后,会将其标记为已认证存入会话中。

如果我们要实现我们自定义的短信验证码的认证,我们需要仿照上图左边的认证流程,实现我们自己的认证流程。我们需要自定义一个SmsAuthenticationFilter,然后自定义实现一个SmsAuthenticationProvider,最重要的还有SmsAuthentincationToken

自定义短信验证码登录

1、自定义token,这个token在登录成功之前先只会存储登录的请求参数,登录成功之后,则会存储登录成功之后的用户信息

/*** autor:liman* createtime:2021/7/11* comment: 短信验证码登录的token(短信验证码登录的登录信息)* 登录之前,存放的是用户的手机号,登录成功之后,其中会存放用户信息* 需要参考 UsernamePasswordAuthenticationToken*/
public class SmsVerifyCodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;//用于存放认证信息的,登录之前存放手机号,登录成功之后,这里会存放用户信息private final Object principal;
//    private Object credentials;//这个用于存放密码(这个实例中用不上)/*** 登录认证之前的构造函数*/public SmsVerifyCodeAuthenticationToken(Object principal) {super(null);this.principal = principal;setAuthenticated(false);}/*** 登录认证chenggong* @param principal* @param authorities*/public SmsVerifyCodeAuthenticationToken(Object principal,Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;
//        this.credentials = credentials;super.setAuthenticated(true); // must use super, as we override}public Object getCredentials() {//        return this.credentials;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();}
}

2、自定义实现SmsAuthenticationFilter

/*** autor:liman* createtime:2021/7/11* comment:用于短信验证码登录的过滤器*/
public class SmsVerifyCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public static final String SELF_SMS_LOGIN_MOBILE = "mobile";private String mobileParameter = SELF_SMS_LOGIN_MOBILE;private boolean postOnly = true;//当前过滤器只处理post请求public SmsVerifyCodeAuthenticationFilter() {//匹配的目标请求super(new AntPathRequestMatcher("/authentication/mobile", "POST"));}/*** 真正的认证流程* @param request* @param response* @return* @throws AuthenticationException*/public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {if (postOnly && !request.getMethod().equals("POST")) {//是否是post请求throw new AuthenticationServiceException("认证的方法不支持" + request.getMethod());}String mobile = obtainMobile(request);if (mobile == null) {mobile = "";}mobile = mobile.trim();//实例化tokenSmsVerifyCodeAuthenticationToken authRequest = new SmsVerifyCodeAuthenticationToken(mobile);// 将请求的信息设置到token中setDetails(request, authRequest);//条用AuthenticationManager进行验证return this.getAuthenticationManager().authenticate(authRequest);}/*** 获取用户名(这里是mobile)* @param request so that request attributes can be retrieved**/protected String obtainMobile(HttpServletRequest request) {return request.getParameter(mobileParameter);}/*** Provided so that subclasses may configure what is put into the authentication* request's details property.** @param request that an authentication request is being created for* @param authRequest the authentication request object that should have its details* set*/protected void setDetails(HttpServletRequest request,SmsVerifyCodeAuthenticationToken authRequest) {authRequest.setDetails(authenticationDetailsSource.buildDetails(request));}/***/public void setMobileParameter(String mobileParameter) {Assert.hasText(mobileParameter, "手机号不能为空");this.mobileParameter = mobileParameter;}/*** 只支持post*/public void setPostOnly(boolean postOnly) {this.postOnly = postOnly;}
}

可以看到,如果想实现更复杂的登录方式,可以自定义obtainMobile方法,这个方法就定义在这个Filter中,可以自行发挥。甚至可以直接读取request请求中的数据流,并将数据流转换成我们自定义的对象都是可行的(笔者最新实际开发的项目中就用到了这个方法)。

3、自定义实现SmsAuthenticationProvider

/*** autor:liman* createtime:2021/7/11* comment:短信验证码登录的认证逻辑*/
public class SmsVerifyCodeAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userDetailsService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {SmsVerifyCodeAuthenticationToken smsVerifyCodeAuthenticationToken = (SmsVerifyCodeAuthenticationToken) authentication;String mobile = (String) smsVerifyCodeAuthenticationToken.getPrincipal();UserDetails userInfo = userDetailsService.loadUserByUsername(mobile);//根据手机号读取用户信息if (null == userInfo) {throw new InternalAuthenticationServiceException("用户信息不存在");}//登录成功,则需要重新构造SmsVerifyCodeAuthenticationTokenSmsVerifyCodeAuthenticationToken authenticationResult = new SmsVerifyCodeAuthenticationToken(userInfo, userInfo.getAuthorities());authenticationResult.setDetails(smsVerifyCodeAuthenticationToken.getDetails());//未认证的token中detail信息也要复制到认证之后的请求信息return authenticationResult;}/*** 当前provider 支持的认证类型是哪一种** @param authentication* @return*/@Overridepublic boolean supports(Class<?> authentication) {//判断传进来的是否是SmsVerifyCodeAuthenticationTokenreturn SmsVerifyCodeAuthenticationToken.class.isAssignableFrom(authentication);}public UserDetailsService getUserDetailsService() {return userDetailsService;}public void setUserDetailsService(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}
}

最为关键的就是authenticate方法,这个方法中在完成认证之后,重新实例化了一个Token对象,并将请求的详细信息赋值给了新建的Token。同时,这里是用UserDetailsService去读取用户信息,这里完全可以自定义

4、配置处理

/*** autor:liman* createtime:2021/7/11* comment:短信验证码的登录配置*/
@Component
public class SmsVerifyCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity> {@Autowiredprivate AuthenticationSuccessHandler selfAuthenticationSuccessHandler;@Autowiredprivate AuthenticationFailureHandler selfAuthenticationFailureHandler;//这个可以是我们自定义的用户服务类@Autowiredprivate UserDetailsService userDetailsService;@Overridepublic void configure(HttpSecurity http) throws Exception {//实例和初始化我们自定义的短信认证FilterSmsVerifyCodeAuthenticationFilter smsVerifyCodeAuthenticationFilter = new SmsVerifyCodeAuthenticationFilter();smsVerifyCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));smsVerifyCodeAuthenticationFilter.setAuthenticationSuccessHandler(selfAuthenticationSuccessHandler);smsVerifyCodeAuthenticationFilter.setAuthenticationFailureHandler(selfAuthenticationFailureHandler);//实例和初始化我们自定义的认证ProviderSmsVerifyCodeAuthenticationProvider smsVerifyCodeAuthenticationProvider = new SmsVerifyCodeAuthenticationProvider();smsVerifyCodeAuthenticationProvider.setUserDetailsService(userDetailsService);//将我们自定义的认证Provider和Filter交给spring-security进行托管,将过滤器插入到UsernamePasswordAuthenticationFilter过滤器之前http.authenticationProvider(smsVerifyCodeAuthenticationProvider).addFilterAfter(smsVerifyCodeAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);}
}

5、将上述配置应用到登录中

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate SecurityProperties securityProperties;//自定义登录成功处理器@Autowiredprivate SelfAuthenticationSuccessHandler selfAuthenticationSuccessHandler;@Autowiredprivate SelfAuthenticationFailureHandler selfAuthenticationFailureHandler;@Autowiredprivate SmsVerifyCodeAuthenticationSecurityConfig smsVerifyCodeAuthenticationSecurityConfig;@Overrideprotected void configure(HttpSecurity http) throws Exception {ImageVerifyCodeFilter imageVerifyCodeFilter = new ImageVerifyCodeFilter();imageVerifyCodeFilter.setAuthenticationFailureHandler(selfAuthenticationFailureHandler);imageVerifyCodeFilter.setSecurityProperties(securityProperties);imageVerifyCodeFilter.afterPropertiesSet();SmsVerifyCodeFilter smsVerifyCodeFilter = new SmsVerifyCodeFilter();smsVerifyCodeFilter.setAuthenticationFailureHandler(selfAuthenticationFailureHandler);smsVerifyCodeFilter.setSecurityProperties(securityProperties);smsVerifyCodeFilter.afterPropertiesSet();http.addFilterBefore(imageVerifyCodeFilter, UsernamePasswordAuthenticationFilter.class).addFilterBefore(smsVerifyCodeFilter, UsernamePasswordAuthenticationFilter.class).formLogin()//采用表单登录.loginPage("/authentication/require")//指定登录的页面.loginProcessingUrl("/authentication/form")//覆盖 UsernamePasswordAuthenticationFilter 中的请求配置,但最终处理这个请求的还是 UsernamePasswordAuthenticationFilter.successHandler(selfAuthenticationSuccessHandler)//自定义登录成功处理器.failureHandler(selfAuthenticationFailureHandler).and().authorizeRequests()//并且要认证请求.antMatchers("/authentication/require", securityProperties.getBrowser().getLoginPage(),"/verifycode/*").permitAll()//登录页的请求不需要认证.anyRequest()//对任意的请求.authenticated()//都需要做认证.and().csrf().disable()//关闭csrf.apply(smsVerifyCodeAuthenticationSecurityConfig);//导入短信验证码登录的安全配置}
}

主要注意最后一行,将我们自定义的配置交给应用的总配置即可。

总结

自定义实现登录方式,可以不只是短信验证码登录。
源码地址——请参看以spring-security开头的文件夹。

spring-security学习(六)——短信验证码登录相关推荐

  1. Spring Security简单增加短信验证码登录

    查网上资料增加短信验证码登录都要增加一大推,要重头写Spring Security的实现,我呢,只想在原来的密码登录基础上简单实现一下短信验证码登录. 1.首先得先一个认证类,来认证验证码是否正确,这 ...

  2. 5.Spring Security 短信验证码登录

    Spring Security 短信验证码登录 在 Spring Security 添加图形验证码一节中,我们已经实现了基于 Spring Boot + Spring Security 的账号密码登录 ...

  3. Spring Security 短信验证码登录(5)

    在Spring Security添加图形验证码中,我们已经实现了基于Spring Boot + Spring Security的账号密码登录,并集成了图形验证码功能.时下另一种非常常见的网站登录方式为 ...

  4. Spring Security OAuth2 优雅的集成短信验证码登录以及第三方登录

    基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring ...

  5. security模仿密码登录实现短信验证码登录

    security模仿密码登录实现短信验证码登录 模仿UsernamePasswordAuthenticationToken创建短信验证码的token类SmsAuthenticationToken /* ...

  6. 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能

    本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...

  7. cas5.3.9自定义手机短信验证码登录

    cas自定义多种登录方式 cas添加手机短信验证码登录 cas添加手机短信验证码登录 全部基于SpringBoot,以及SpringWebflow开发,请在有此基础再进行学习! 添加Maven依赖 & ...

  8. OAuth2.0 - 自定义模式授权 - 短信验证码登录

    一.OAuth2.0 - 自定义模式授权 上篇文章我们分析了目前的情况,演示了微服务的大环境下在保证安全的情况下通过SpringGateWay实现统一的鉴权处理,但是前面的演示中,我们都是基于用户名密 ...

  9. SpringBoot OAuth2.0 使用短信验证码登录授权

    SpringBoot OAuth2.0 使用短信验证码登录授权 实现步骤: 自定义授权器,继承 AbstractTokenGranter 类: 重写 getOAuth2Authentication 函 ...

最新文章

  1. 哪些人交不到真朋友?
  2. linux撤销编辑内容,linux编辑利器vim常用操作
  3. 基于元组,根据月份,计算天数.(Python)
  4. Android-标题状态栏的隐藏
  5. 问题来了:哪个AI公司会签下张学友当代言人?
  6. Python的lambda, filter, reduce 和 map简介
  7. HDU 6030(矩阵快速幂+规律)
  8. java(8) HashMap源码
  9. Java二级知识总结
  10. Kindle刷安卓双系统的方法
  11. kali安装有道词典
  12. Learn OpenCV之Heatmap
  13. AttributeError: module cv2.face has no attribute 'createEigenFaceRecognizer'
  14. python 矩阵化为最简阶梯型
  15. HashMap底层源码解析
  16. 数说亚洲杯小组赛:冷门四宗“最”知多少?
  17. 高新技术企业是什么?
  18. 基于工业物联网的工业机器人PHM架构
  19. 旅行世界显示服务器繁忙,旅行世界,玩法介绍!无充值入口,每日遛狗赚
  20. 论文解读--K-Radar:4D Radar Object Detection for Autonomous Driving in Various Weather Conditions

热门文章

  1. book-----好书~~~~
  2. php 取整 ceil,php取整函数ceil、floor、round、intval用法区别
  3. 哈希碰撞与生日相同概率
  4. Idea Gradle配置阿里源
  5. HTML循环数据表格
  6. 【计算机毕业文章】垃圾分类系统设计与实现
  7. 云计算 Linux系统配置及服务管理 (一)系统部署
  8. jmeter 性能测试 结果分析
  9. 气象数据Grib格式解析的Python代码和Matlab代码
  10. Rethinking eventual consistency论文部分段落翻译