security模仿密码登录实现短信验证码登录
- 模仿UsernamePasswordAuthenticationToken创建短信验证码的token类SmsAuthenticationToken
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 531L;
private final Object principal;
private Object credentials;
public SmsAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public SmsAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return credentials;
}
@Override
public Object getPrincipal() {
return principal;
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
- 模仿UsernamePasswordAuthenticationFilter创建处理短信验证码的过滤器
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = "username";
private String passwordParameter = "password";
private boolean postOnly = true;
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter);
}
@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return this.usernameParameter;
}
public final String getPasswordParameter() {
return this.passwordParameter;
}
}
- 模仿DaoAuthenticationProvider创建处理SmsAuthenticationToken的Provider
public class SmsAuthenticationProvider implements AuthenticationProvider {
private SmsUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final SmsAuthenticationToken smsAuthenticationToken = (SmsAuthenticationToken) authentication;
final String mobile = smsAuthenticationToken.getPrincipal() == null ? "" :
smsAuthenticationToken.getPrincipal().toString();
final String code = smsAuthenticationToken.getCredentials().toString();
if (code == null) {
throw new SmsAuthenticationException(SmsAuthenticationHandler.AuthenticationStatus.NO_VERIFY_CODE);
}
checkSmsCode(mobile, code);
final UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
return createSuccessAuthenticationToken(smsAuthenticationToken, userDetails);
}
private SmsAuthenticationToken createSuccessAuthenticationToken(SmsAuthenticationToken smsAuthenticationToken, UserDetails userDetails) {
final SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(smsAuthenticationToken.getDetails());
return authenticationToken;
}
public void setUserDetailsService(SmsUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
private void checkSmsCode(String mobile, String code) {
final boolean flag = userDetailsService.checkSmsCode(mobile, code);
if (!flag) {
throw new SmsAuthenticationException(SmsAuthenticationHandler.AuthenticationStatus.BAD_VERIFY_CODE);
}
}
@Override
public boolean supports(Class<?> aClass) {
return SmsAuthenticationToken.class.isAssignableFrom(aClass);
}
}
- 模仿UserDetailsService
public interface SmsUserDetailsService extends UserDetailsService {
boolean checkSmsCode(String mobile, String code);
}
- 创建配置类SmsCodeAuthenticationSecurityConfig
@Configuration
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private SmsAuthenticationHandler smsAuthenticationHandler;
@Autowired
private MobileUserDetailsServiceImpl mobileUserDetailsService;
@Override
public void configure(HttpSecurity http) throws Exception {
final SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsCodeAuthenticationFilter.setAuthenticationDetailsSource(new ExtendWebAuthenticationDetailsSource());
smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(smsAuthenticationHandler);
smsCodeAuthenticationFilter.setAuthenticationFailureHandler(smsAuthenticationHandler);
final SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();
smsAuthenticationProvider.setUserDetailsService(mobileUserDetailsService);
http.authenticationProvider(smsAuthenticationProvider)
.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
- 创建配置类WebSecurityConfig继承WebSecurityConfigurerAdapter,实现void configure(HttpSecurity http)方法。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.apply(smsCodeAuthenticationSecurityConfig);
http.csrf().disable();
}