At present, common social software, shopping software, payment software, financial software, etc., require users to log in to enjoy the services provided by the software. There are three main login methods: account password login, SMS verification code login, and third-party authorization login. We have implemented account password and third party authorized login. In this chapter, we will use Spring Security to implement SMS verification code login.

An overview of the

In Spring Security source analysis one: Spring Security authentication process and Spring Security source analysis two: Spring Security authorization process in two chapters. We’ve looked at how Spring Security handles username and password logins in detail. In this chapter we will display SMS logins modeled after user names and passwords.

The directory structure

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/14/160f3abd6445be85~tplv-t2oaga2asx-image.image

SmsCodeAuthenticationFilter

SmsCodeAuthenticationFilter corresponding user name password login UsernamePasswordAuthenticationFilter same AbstractAuthenticationProcessingFilter inheritance

public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /** * Request must contain the mobile parameter */
    private String mobileParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE;
    /** * Post request */
    private boolean postOnly = true;

    protected SmsCodeAuthenticationFilter(a) {
        /** * Handle mobile verification code login request handle URL */
        super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE, "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        // Check whether it is a POST request
        if(postOnly && ! request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        // Get the mobile phone number from the request
        String mobile = obtainMobile(request);

        if (mobile == null) {
            mobile = "";
        }

        mobile = mobile.trim();
        / / create SmsCodeAuthenticationToken (unauthorized)
        SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);

        // Set user information
        setDetails(request, authRequest);
        // Return Authentication instance
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    /** * Obtain the mobile phone number */
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }

    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setMobileParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.mobileParameter = usernameParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getMobileParameter(a) {
        returnmobileParameter; }}Copy the code
  1. The method of authenticating the request must bePOST
  2. Obtain the mobile phone number from request
  3. Encapsulate your ownAuthenticaitonThe implementation of the classSmsCodeAuthenticationToken(Uncertified)
  4. callAuthenticationManagerauthenticateMethod to validate (i.eSmsCodeAuthenticationProvider)

SmsCodeAuthenticationToken

UsernamePasswordAuthenticationToken SmsCodeAuthenticationToken corresponding user name password to log in

public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 2383092775910246006L;

    /** * Mobile phone number */
    private final Object principal;

    / * * * SmsCodeAuthenticationFilter build not certification Authentication *@param mobile
     */
    public SmsCodeAuthenticationToken(String mobile) {
        super(null);
        this.principal = mobile;
        setAuthenticated(false);
    }

    / * * * SmsCodeAuthenticationProvider Authentication of building certified *@param principal
     * @param authorities
     */
    public SmsCodeAuthenticationToken(Object principal, Collection
        authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true); // must use super, as we override
    }

    @Override
    public Object getCredentials(a) {
        return null;
    }

    @Override
    public Object getPrincipal(a) {
        return this.principal;
    }

    / * * *@param isAuthenticated
     * @throws IllegalArgumentException
     */
    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(a) {
        super.eraseCredentials(); }}Copy the code

SmsCodeAuthenticationProvider

SmsCodeAuthenticationProvider corresponding user name password DaoAuthenticationProvider

public class SmsCodeAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
        // Invoke the user-defined userDetailsService authentication
        UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());

        if (user == null) {
            throw new InternalAuthenticationServiceException("Unable to obtain user information");
        }
        / / if the user is not empty to rebuild SmsCodeAuthenticationToken (certified)
        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());

        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }
	
	Only the Authentication / * * * * for SmsCodeAuthenticationToken use this Provider Authentication@param authentication
     * @return* /
    @Override
    public boolean supports(Class
        authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public UserDetailsService getUserDetailsService(a) {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService; }}Copy the code

SmsCodeAuthenticationSecurityConfig text login configuration

@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain.HttpSecurity> {

    @Autowired
    private AuthenticationFailureHandler merryyouAuthenticationFailureHandler;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        / / custom SmsCodeAuthenticationFilter filter
        SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        smsCodeAuthenticationFilter.setAuthenticationFailureHandler(merryyouAuthenticationFailureHandler);

        / / set custom SmsCodeAuthenticationProvider certifier userDetailsService
        SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
        smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
        / / before UsernamePasswordAuthenticationFilter filtering is carried outhttp.authenticationProvider(smsCodeAuthenticationProvider) .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); }}Copy the code

MerryyouSecurityConfig main configuration file

 @Override
    protected void configure(HttpSecurity http) throws Exception {
// http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
        http
                .formLogin()// Use form login instead of default httpBasic
                .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)// The URL to jump to if the requested URL requires authentication
                .loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)// Handle the custom login URL in the form
                .and()
                .apply(validateCodeSecurityConfig)// Captcha interception
                .and()
                .apply(smsCodeAuthenticationSecurityConfig)
                .and()
                .apply(merryyouSpringSocialConfigurer)// Social login
                .and()
                .rememberMe()
......
Copy the code

Debugging process

SMS login interception request /authentication/ Mobile

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/14/160f3abd6467cddd~tplv-t2oaga2asx-image.image

Custom SmsCodeAuthenticationProvider

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/14/160f3abd63fdfb98~tplv-t2oaga2asx-image.image

The effect is as follows:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/14/160f3abd5b178799~tplv-t2oaga2asx-image.image

The code download

Download it from my Github, github.com/longfeizhen…