1.原理
通过之前的分析我们理解了密码登入的整个逻辑,短信验证码登入参考密码登入
根据原理我们构建对应的类
2.步骤
- 构建SmsAuthenticationToken ,参照UsernamePasswordAuthenticationToken
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
public SmsAuthenticationToken(Object principal) {
super(null);
this.principal = principal;
setAuthenticated(false);
}
public SmsAuthenticationToken( Object principal,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true); // must use super, as we override
}
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);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return principal;
}
}
- 构建SmsAuthenticationFilter 同样参考UsernamePasswordAuthenticationFilter
@Data
public class SmsAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
private boolean postOnly = true;
public SmsAuthenticationFilter() {
super(new AntPathRequestMatcher("/authentication/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());
}
String mobile = obtainMobile(request);
if (mobile == null) {
mobile = "";
}
mobile = mobile.trim();
SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainMobile(HttpServletRequest request) {
return request.getParameter(mobileParameter);
}
protected void setDetails(HttpServletRequest request,
SmsAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
}
- SmsAuthenticationProvider 参考 DaoAuthenticationProvider 写自己的校验逻辑
@Data
public class SmsAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsAuthenticationToken smsAuthenticationToken = (SmsAuthenticationToken)authentication;
String mobile = (String) smsAuthenticationToken.getPrincipal();
//根据手机查询用户信息
UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
if (userDetails == null) {
throw new InternalAuthenticationServiceException("用户信息不存在");
}
SmsAuthenticationToken result = new SmsAuthenticationToken(userDetails,userDetails.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (SmsAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
- 配置信息SmsAuthenticationSecurityConfig
@Component
public class SmsAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity> {
@Autowired
private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler imoocAuthenticationFailureHandler;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(HttpSecurity http) throws Exception {
SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();
//为过滤器添加认证器
smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
//重写认证失败时的跳转页面
smsAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
smsAuthenticationFilter.setAuthenticationSuccessHandler(imoocAuthenticationSuccessHandler);
SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();
smsAuthenticationProvider.setUserDetailsService(userDetailsService);
http.authenticationProvider(smsAuthenticationProvider)
.addFilterAfter(smsAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);
}
}
- 将配置信息加入到springSecurity 配置中