SMS认证
1. Sms认证原理
1.用户前端发起请求,获取短信验证码。后端通过SmsController返回结果,并将验证码保存在session中(如果是无状态就保存在缓存中,设置过期时间)。
2.用户用手机号和验证码发起登录请求,通过SmsVaildataFilter过滤器进行校验,如果失败返回失败结果,校验成功后继续往下一个过滤器走。
3.通过SmsAuthencationFilter调用UserService进行认证。
2. SmsAuthencationFilter
2.1 Sms用户认证概述
在短信验证登录的过程中,前边的各种校验相对简单,而SmsAuthencationFilter中的各种方法需要理解。
首先在SpringSecurity中认证和鉴权是两种操作。根据用户手机号获取验证码校验验证码,仅仅只是对验证码的操作,并没有实现用户的认证。校验完了验证码没有认证对于服务器内部的接口还是不能够进行访问,并没有什么卵用,说白了就是前边的前边的操作只是看了看用户的验证码和手机号匹配么,SmsAuthencationFilter中的操作就是实现用户的认证。
何为用户的认证?我理解的用户认证就是通过一系列的操作,将用户的信息保存在Authentication对象中,通过authenticated值(true,false)来判断用户是否被认证。(划重点,在SpringSecurity框架中见到的Authentication对象中保存的都是对象的主体)。
2.2 Sms用户认证的过程
微观上看这个过程就是对SmsCodeAuthenticationToken的一系列操作,先是将用户手机号保存在Token中,然后通过认证将用户的数据保存在Token中,其中authenticated值为true时就是校验通过。
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//判断请求是否是post请求
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
//获取请求中的参数,obtainMobile是定义的方法获取参数。
String mobile = this.obtainMobile(request);
if (mobile == null) {
mobile = "";
}
mobile = mobile.trim();
//生成Token,此时Token中只有手机号
SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
//通过此方法将数据封装到Authentication中
this.setDetails(request, authRequest);
//调用authenticate方法校验,(SmsCodeAuthenticationProvider中的方法)
return this.getAuthenticationManager().authenticate(authRequest);
}
}
1.通过层层过滤器,我们的手机号来到了最关键的过滤器中,SmsAuthenticationFilter,首先通过SmsCodeAuthenticationToken生成一个Token对象,将手机号保存在里边。
2.调用SmsCodeAuthenticationProvider中的authenticate方法进行验证,authenticate方法调用的UserDetailService中的loadByUsername方法返回UserDetails类型的对象。再次成SmsCodeAuthenticationToken对象,将用户数据保存在Token中,包装成Authention返回给SmsAuthenticationFilter
3.Security上下文调度,执行下一个过滤操作。
//获取保存在Authentication中的用户数据(在SmsAuthenticationFilter中通过attemptAuthentication方法调用,只保存了手机号)
SmsCodeAuthenticationToken authenticationToken=(SmsCodeAuthenticationToken) authentication;
//通过手机号进行查询用户的信息
UserDetails userDetails=userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
if (userDetails==null){
throw new InternalAuthenticationServiceException("无法根据手机号获取用户信息");
}
//再次成SmsCodeAuthenticationToken对象,这次里边存储的是用户信息。这个构造方法会调用setAuthenticate(true)表示认证通过了
SmsCodeAuthenticationToken authenticationResult=new SmsCodeAuthenticationToken(userDetails,userDetails.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
3. SmsCodeSecurityConfig
经过了一系列的操作,什么过滤器,各种校验。。。。都搭建好了。
考虑一个问题:当前端发起短信登录请求时,凭什么就得用SmsAuthenticaionFilter,这个过滤器来处理认证请求呢?为什么不用UsernamePasswordFilter处理? (黑人???.jpg)
认证过滤器是需要配置的,需要我们手动的告诉SpringSecurity,不废话直接上代码。
SmsAuthenticationFilter smsAuthenticationFilter=new SmsAuthenticationFilter();
//认证管理器通过认证必选要有,作用是通过遍历选择合适的Provider
smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
//配置认证成功的处理操作
smsAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
//配置认证失败的处理操作
smsAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
//配置Provider重点是边的authenticate方法
SmsCodeAuthenticationProvider smsCodeAuthenticationProvider=new SmsCodeAuthenticationProvider();
//给Provider配置自己的UserDetailService
smsCodeAuthenticationProvider.setUserDetailsService(myUserDetailsService);
//将验证码校验的过滤器配置到UsernamePasswordAuthenticationFilter前边
http.addFilterBefore(smsCodeValidateFilter, UsernamePasswordAuthenticationFilter.class);
//配置Provider重点是边的authenticate方法
http.authenticationProvider(smsCodeAuthenticationProvider)
//将用户认证的过滤器配置到UsernamePasswordAuthenticationFilter后边 .addFilterAfter(smsAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);
重点说一下,过滤器链的配置,用户发起"/smslogin"请求先进行验证码的校验,然后走UsernamePasswordAuthenticationFilter请求发现不是"/login"请求,走下一个过滤器,来到了SmsAuthenticationFilter进行校验。然后认证成功,返回数据,在进行access鉴权。
最后需要在WebSecurityConfigurerAdapter中装配上smsCodeSecurityConfig
以上内容依据本人理解所写,如有偏差欢迎指正,相互学习,没有实现发送短信的第三方接口。在控制台打印。
源码位置:SpringSecurity