Implementation principle
In previous articles, we introduced the method of logging in with ordinary account passwords: SpringBoot + Spring Security Basic usage and personalized login configuration. But there is another common way now, which is to log in directly through the SMS verification code of your mobile phone. Here you need to do some extra work by yourself.
Anyone who has a certain understanding of the detailed explanation of the SpringSecurity authentication process knows that in the process of account password authentication, the following categories are involved: UsernamePasswordAuthenticationFilter (used to request parameter acquisition), UsernamePasswordAuthenticationToken (represents user login information), ProviderManager (for authentication verification),
Because it is logged in through the SMS verification code, we need to rewrite the requested parameters, authentication process, and user login token information for a certain amount.
Of course, we should put the verification code process first, if the graphic verification code is implemented the same. The advantage of this approach is that the verification code authentication process is decoupled so that other interfaces can also be used.
Basic implementation
Verification code verification
The functional implementation of SMS verification code is actually the same as the principle of graphic verification code. It's just that one is to return a picture to the front end, and the other is to send a short message to the user. Here you just need to call the interface of the SMS service provider. For more principles, please refer to SpringBoot + SpringSecurity to implement the graphical verification code function.
AuthenticationToken
When logging in with your account password, the UsernamePasswordAuthenticationToken contains the user's account, password, and other status information such as whether it is available. We log in through SMS on mobile phone, so we don’t have a password. Here we just copy the UsernamePasswordAuthenticationToken code and remove the password-related information.
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final Object principal; public SmsCodeAuthenticationToken(String mobile) { super(null); this.principal = mobile; setAuthenticated(false); } public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; super.setAuthenticated(true); // must use super, as we override } public Object getCredentials() { 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); } @Override public void eraseCredentials() { super.eraseCredentials(); }} AuthenticationFilter
In the process of logging in the account password, the default is UsernamePasswordAuthenticationFilter. Its function is to obtain the account and password from the request, verify the request method, and generate an AuthenticationToken. Here our parameters have changed a certain way, so it is still the old method, copy to make simple modifications
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // Request parameters key private String mobileParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE; // Is it only supported POST private boolean postOnly = true; public SmsCodeAuthenticationFilter() { // Request the interface's url super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_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()); } // Obtain the request value according to the request parameter name String mobile = obtainMobile(request); if (mobile == null) { mobile = ""; } mobile = mobile.trim(); // Generate the corresponding AuthenticationToken SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * Get the mobile number*/ protected String obtainMobile(HttpServletRequest request) { return request.getParameter(mobileParameter); } // Omit irrelevant code} Provider
During the login process of account password, the correctness of the password and whether the account is available are verified through the DaoAuthenticationProvider. We should also implement a provider ourselves
public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; /** * Identity logic verification* @param authentication * @return * @throws AuthenticationException */ @Override public Authentication authentication(Authentication authentication) throws AuthenticationException { SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication; UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); if (user == null) { throw new InternalAuthenticationServiceException("User information cannot be obtained"); } SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } @Override public boolean supports(Class<?> authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; }} Configuration
The main authentication process is achieved through the above four processes. Here we can reduce their configuration.
@Componentpublic class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired private AuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private AuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired private UserDetailsService userDetailsService; @Override public void configure(HttpSecurity http) throws Exception { SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter(); smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler); SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider(); smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(smsCodeAuthenticationProvider) .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); }} // BrowerSecurityConfig.java@Overrideprotected void configure(HttpSecurity http) throws Exception { http.apply(smsCodeAuthenticationSecurityConfig);} Code download
Spring-Security
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.