Spring security spring boot + spring security SMS confirmation

Keywords: Programming Mobile Spring Redis less

Now there are three main login methods: account password login, SMS verification code login and third-party authorized login. The previous section Spring security (3) -- authentication process We have analyzed the spring security account password mode login. Now let's analyze the spring security SMS mode authentication login.

                        

  1. Custom Filter:

  2. Custom Authentication

  3. Custom AuthenticationProvider

  4. Custom UserDetailsService

  5. SecurityConfig configuration

1. User defined filter:

                          

  1. Build the constructor and filter the request path and request mode in the constructor

  2. Custom attemptAuthentication() authentication steps

  3. In step 2, the authentication provider is required for final authentication. In the authentication filter, the AuthenticationProvider needs to be set into the filter, while the AuthenticationManager is used to manage the AuthenticationProvider. Therefore, when creating the filter filter, we need to set the AuthenticationManager. This step is detailed in the 5.1 SecurityConfig configuration step.

In step 2, the attamptauthentication() authentication method mainly performs the following steps:
   1).post request authentication;
    2).request to obtain mobile phone number and verification code;
    3). Encapsulate mobile phone number and verification code with user-defined Authentication object;
   4). Use the AuthenticationManager.authenticate() method to verify.

Custom filter implementation code:

public class SmsAuthenticationfilter extends AbstractAuthenticationProcessingFilter {    private boolean postOnly = true;    public SmsAuthenticationfilter() {      super(new AntPathRequestMatcher(SecurityConstants.APP_MOBILE_LOGIN_URL, "POST"));
   }


    [@Override](https://my.oschina.net/u/1162528)
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {        if (postOnly && !request.getMethod().equals("POST")) {              throw new AuthenticationServiceException(                   "Authentication method not supported: " + request.getMethod());
      }
        Assert.hasText(SecurityConstants.MOBILE_NUMBER_PARAMETER, "mobile parameter must not be empty or null");
     
         String mobile = request.getParameter(SecurityConstants.MOBILE_NUMBER_PARAMETER);
        String smsCode = request.ge+tParameter(SecurityConstants.MOBILE_VERIFY_CODE_PARAMETER);        if (mobile == null) {
            mobile="";
        }        if(smsCode == null){
            smsCode="";
        }
        mobile = mobile.trim();
        smsCode = smsCode.trim();
        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile,smsCode);        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);    
        return this.getAuthenticationManager().authenticate(authRequest);
    }protected void setDetails(HttpServletRequest request,
                              SmsAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }    public void setPostOnly(boolean postOnly) {        this.postOnly = postOnly;
    }

}

2. Authentication:

                             . https://www.jianshu.com/p/5e19f3a9f6dd

Custom SmsAuthenticationToken:

public class SmsAuthenticationToken extends AbstractAuthenticationToken {    private final Object principal;    private Object credentials;    public SmsAuthenticationToken(Object principal,Object credentials ) {        super(null);        this.principal = principal;        this.credentials=credentials;
        setAuthenticated(false);
    }    public SmsAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) {        super(null);        this.principal = principal;        this.credentials=credentials;
        setAuthenticated(true);
    }

    [@Override](https://my.oschina.net/u/1162528)
    public Object getCredentials() {        return this.credentials=credentials;
    }

    [@Override](https://my.oschina.net/u/1162528)
    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](https://my.oschina.net/u/1162528)
    public void eraseCredentials() {        super.eraseCredentials();

    }
}

3.AuthenticationProvider

The final authentication policy entry of                    . You can write according to AbstractUserDetailsAuthenticationProvider to implement the AuthenticationProvider and MessageSourceAware interface. The authentication logic can define the implementation. Is Jiaozuo Guoyitang gastrointestinal hospital normal: http://jz.lieju.com/xuankeyiyuan/37174867.htm

Custom AuthenticationProvider:

public class SmsAuthenticationProvide implements AuthenticationProvider, MessageSourceAware {  private UserDetailsService userDetailsService;  private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

    [@Override](https://my.oschina.net/u/1162528)
    public void setMessageSource(MessageSource messageSource) {        this.messages = new MessageSourceAccessor(messageSource);
    }    @Override
    public Authentication authenticate(Authentication authentication) {
        Assert.isInstanceOf(SmsAuthenticationToken.class, authentication,
                messages.getMessage(                        "AbstractUserDetailsAuthenticationProvider.onlySupports",                        "Only UsernamePasswordAuthenticationToken is supported"));
        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;        //Save the authentication information in the SecurityContext for the UserDetailsService to verify
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authenticationToken);
        String mobile = (String) authenticationToken.getPrincipal();        if (mobile == null) {            throw new InternalAuthenticationServiceException("can't obtain user info ");
        }
        mobile = mobile.trim();        //Verify and obtain user information
        UserDetails user = userDetailsService.loadUserByUsername(mobile);        if (user == null) {            throw new InternalAuthenticationServiceException("can't obtain user info ");
        }
        SmsAuthenticationToken smsAuthenticationToken = new SmsAuthenticationToken(user, user.getAuthorities());        return smsAuthenticationToken;
    }    @Override
    public boolean supports(Class<?> authentication) {        return (SmsAuthenticationToken.class.isAssignableFrom(authentication));
    }    public void setUserDetailsService(UserDetailsService userDetailsService) {        this.userDetailsService = userDetailsService;
    }    public UserDetailsService getUserDetailsService() {        return userDetailsService;
    }
}

4. UserDetailsService

                     . You can customize the authentication logic according to your project.

Custom UserDetailsService:

public class SmsUserDetailsService implements UserDetailsService {    @Autowired
    private RedisUtil redisUtil;    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        //Obtain the information (mobile number, verification code) required for authentication from SecurityContext
        SecurityContext context = SecurityContextHolder.getContext();
        SmsAuthenticationToken authentication = (SmsAuthenticationToken) context.getAuthentication();        if(!additionalAuthenticationChecks(username,authentication)){            return null;
        }        //Obtain the information of the corresponding user of the user's mobile number, including the authority, etc
        return new User("admin", "123456", Arrays.asList(new SimpleGrantedAuthority("admin")));
    }    public boolean additionalAuthenticationChecks(String mobile, SmsAuthenticationToken smsAuthenticationToken) {        //Get the value verification code corresponding to the mobile key value in redis
        String smsCode = redisUtil.get(mobile).toString();        //Get the verification code submitted by the user
        String credentials = (String) smsAuthenticationToken.getCredentials();        if(StringUtils.isEmpty(credentials)){            return false;
        }        if (credentials.equalsIgnoreCase(smsCode)) {            return true;
        }        return false;
    }
}

5.SecurityConfig

5.1 configure SecurityConfig for Sms authentication component

                             

  1. Set the default AuthenticationManager (which can also be defined) to a custom filter

  2. Set the custom UserDetailsService to the custom authenticationprovider for use

  3. Add the filter to the filtering link to implement the filtering operation. (usually added before UsernamePasswordAuthenticationFilter)

Configure SmsAuthenticationSecurityConfig:

 @Component
 public class SmsAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {    @Autowired
    private UserDetailsService userDetailsService;    @Override
    public void configure(HttpSecurity http) throws Exception {        //Create and configure a custom SmsAuthenticationfilter,
        SmsAuthenticationfilter smsAuthenticationfilter = new SmsAuthenticationfilter();
        smsAuthenticationfilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        smsAuthenticationfilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler());
        smsAuthenticationfilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler());        //Create and configure a custom smsauthenticationprovider
        SmsAuthenticationProvide smsAuthenticationProvide=new SmsAuthenticationProvide();
        smsAuthenticationProvide.setUserDetailsService(userDetailsService);
        http.authenticationProvider(smsAuthenticationProvide);        //Add filter to filter link
        http.addFilterAfter(smsAuthenticationfilter, UsernamePasswordAuthenticationFilter.class);
    }    @Bean
    public CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler() {        return new CustomAuthenticationSuccessHandler();
    }    
    @Bean
    public CustomAuthenticationFailureHandler customAuthenticationFailureHandler() {        return new CustomAuthenticationFailureHandler();
    }
}

5.2 SecurityConfig main configuration

Please refer to Section 2 for the main SecurityConfig configuration Spring Security (2) - WebSecurityConfigurer configuration and filter order Configure.

SecurityConfig main configuration:

@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired
    private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;    @Autowired
    private CustomAuthenticationSuccessHandler authenticationSuccessHandler;    @Autowired
    private CustomAuthenticationFailureHandler authenticationFailureHandler;    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable().and()
                .formLogin()
                .loginPage(SecurityConstants.APP_FORM_LOGIN_PAGE)                //Configure custom URL for form login
                .loginProcessingUrl(SecurityConstants.APP_FORM_LOGIN_URL)
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                .and()                //Configure smsAuthenticationSecurityConfig
                .apply(smsAuthenticationSecurityConfig)
                .and()                //Run through URL
                .authorizeRequests()
                .antMatchers(SecurityConstants.APP_MOBILE_VERIFY_CODE_URL,
                             SecurityConstants.APP_USER_REGISTER_URL)
                .permitAll()
                .and()
                .csrf().disable();
    }    @Bean
    public ObjectMapper objectMapper(){        return new ObjectMapper();
    }
}

6. other

6.1 redis

RedisUtil tool class:

@Componentpublic class RedisUtil {    @Autowired
    private RedisTemplate<String, Object> redisTemplate;    /**
     * General cache access
     *
     * @param key key
     * @return value
     */
    public Object get(String key) {        return key == null ? null : redisTemplate.opsForValue().get(key);
    }    /**
     * Normal cache put
     *
     * @param key   key
     * @param value value
     * @return true Success false failure
     */
    public boolean set(String key, Object value) {        try {
            redisTemplate.opsForValue().set(key, value);            return true;
        } catch (Exception e) {
            e.printStackTrace();            return false;
        }
    }    /**
     * Normal cache put and set time
     *
     * @param key   key
     * @param value value
     * @param time  Time (seconds) time should be greater than 0. If time is less than or equal to 0, infinite period will be set
     * @return true Success false failure
     */
    public boolean set(String key, Object value, long time) {        try {            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }            return true;
        } catch (Exception e) {
            e.printStackTrace();            return false;
        }
    }
 }

redisConfig configuration class:

@Configurationpublic class RedisConfig {@Autowiredprivate RedisProperties properties;@Bean@SuppressWarnings("all")@ConditionalOnClass(RedisConnectionFactory.class)public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();        //The key adopts String serialization
        template.setKeySerializer(stringRedisSerializer);        //The key of hash also adopts String serialization
        template.setHashKeySerializer(stringRedisSerializer);        //value serialization is jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);        //value serialization of hash adopts jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();        return template;
    }    @Bean
    @Qualifier("redisConnectionFactory")    public RedisConnectionFactory redisConnectionFactory(){
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        redisConfig.setHostName(properties.getHost());
        redisConfig.setPort(properties.getPort());
        redisConfig.setPassword(RedisPassword.of(properties.getPassword()));
        redisConfig.setDatabase(properties.getDatabase());        //redis connection pool data settings
        JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();        if (this.properties.getTimeout() != null) {
            Duration timeout = this.properties.getTimeout();
            builder.readTimeout(timeout).connectTimeout(timeout);
        }
        RedisProperties.Pool pool = this.properties.getJedis().getPool();        if (pool != null) {
            builder.usePooling().poolConfig(this.jedisPoolConfig(pool));
        }
        JedisClientConfiguration jedisClientConfiguration = builder.build();        //Generate JedisConnectionFactory according to two configuration classes
        return new JedisConnectionFactory(redisConfig,jedisClientConfiguration);

    }    private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(pool.getMaxActive());
        config.setMaxIdle(pool.getMaxIdle());
        config.setMinIdle(pool.getMinIdle());        if (pool.getMaxWait() != null) {
            config.setMaxWaitMillis(pool.getMaxWait().toMillis());
        }        return config;
    }
}

7. summary

                      . In the future, there will be a case study of the third-party landing mode. Last error please comment!


Posted by aissa on Thu, 14 Nov 2019 20:19:01 -0800