1. Configuration related
1.1 AbstractChannelSecurityConfig
Abstract class encapsulating authentication logic for Http pages
ApplyPasswordAuthentication Config sets the default form login authorization authentication route for HttpSecurity, and imoocAuthentication SuccessHandler, imoocAuthentication FailureHandler
extends WebSecurityConfigurerAdapter
implements WebSecurityConfigurer
extends SecurityConfigurer<Filter, T>
applyPasswordAuthenticationConfig
/** * */ package com.wxm.spring.security.core.authentication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import com.wxm.spring.security.core.properties.SecurityConstants; /** * @author zhailiang * */ public class AbstractChannelSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired protected AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; @Autowired protected AuthenticationFailureHandler imoocAuthenticationFailureHandler; protected void applyPasswordAuthenticationConfig(HttpSecurity http) throws Exception { http.formLogin() .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM) .successHandler(imoocAuthenticationSuccessHandler) .failureHandler(imoocAuthenticationFailureHandler); } }
1.2 BrowserSecurityConfig
- @Autowired loads SecurityProperties and reads the configuration items in application.properties
- Inherit AbstractChannelSecurityConfig and call AbstractChannelSecurityConfig.applyPasswordAuthenticationConfig in configure to implement browser-specific settings:
Authentication code configuration:.apply(validateCodeSecurityConfig)
SMS Configuration:.Apply (smsCodeAuthentication SecurityConfig)
Remember my configuration:
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
.userDetailsService(userDetailsService)
Whitelist Configuration:
.authorizeRequests()
.antMatchers(
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
securityProperties.getBrowser().getLoginPage(),
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",
//securityProperties.getBrowser().getSignUpUrl(),
//securityProperties.getBrowser().getSession().getSessionInvalidUrl()+".json",
//securityProperties.getBrowser().getSession().getSessionInvalidUrl()+".html",
"/user/regist")
.permitAll()
.anyRequest()
.authenticated()
Turn off CSFR:
.csrf().disable();
extends AbstractChannelSecurityConfig
configure
```java /** * */ package com.wxm.spring.security.browser; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import com.wxm.spring.security.core.authentication.AbstractChannelSecurityConfig; import com.wxm.spring.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig; import com.wxm.spring.security.core.properties.SecurityConstants; import com.wxm.spring.security.core.properties.SecurityProperties; import com.wxm.spring.security.core.validate.code.ValidateCodeSecurityConfig; /** * @author zhailiang * */ @Configuration public class BrowserSecurityConfig extends AbstractChannelSecurityConfig { @Autowired private SecurityProperties securityProperties; @Autowired private DataSource dataSource; @Autowired private UserDetailsService userDetailsService; @Autowired private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Autowired private ValidateCodeSecurityConfig validateCodeSecurityConfig; //@Autowired //private SpringSocialConfigurer imoocSocialSecurityConfig; //@Autowired //private SessionInformationExpiredStrategy sessionInformationExpiredStrategy; //@Autowired //private InvalidSessionStrategy invalidSessionStrategy; @Override protected void configure(HttpSecurity http) throws Exception { applyPasswordAuthenticationConfig(http); http .apply(validateCodeSecurityConfig) .and() .apply(smsCodeAuthenticationSecurityConfig) .and() //.apply(imoocSocialSecurityConfig) //.and() .rememberMe() .tokenRepository(persistentTokenRepository()) .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds()) .userDetailsService(userDetailsService) .and() //.sessionManagement() //.invalidSessionStrategy(invalidSessionStrategy) //.maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) //.maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin()) //.expiredSessionStrategy(sessionInformationExpiredStrategy) //.and() //.and() .authorizeRequests() .antMatchers( SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, securityProperties.getBrowser().getLoginPage(), SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*", //securityProperties.getBrowser().getSignUpUrl(), //securityProperties.getBrowser().getSession().getSessionInvalidUrl()+".json", //securityProperties.getBrowser().getSession().getSessionInvalidUrl()+".html", "/user/regist") .permitAll() .anyRequest() .authenticated() .and() .csrf().disable(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository.setDataSource(dataSource); // tokenRepository.setCreateTableOnStartup(true); return tokenRepository; } }
1.3 ValidateCodeSecurityConfig
Insert validateCodeFilter before AbstractPreAuthenticatedProcessingFilter
extends SecurityConfigurerAdapter
implements SecurityConfigurer
configure
/** * */ package com.wxm.spring.security.core.validate.code; import javax.servlet.Filter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.stereotype.Component; /** * @author zhailiang * */ @Component("validateCodeSecurityConfig") public class ValidateCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired private Filter validateCodeFilter; @Override public void configure(HttpSecurity http) throws Exception { http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class); } }
1.4 SmsCodeAuthenticationSecurityConfig
Configure SmsCodeAuthenticationFilter to insert SmsCodeAuthenticationFilter before UsernamePasswordAuthenticationFilter
Configure SmsCodeAuthentication Provider
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityConfigurer
configure
/** * */ package com.wxm.spring.security.core.authentication.mobile; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.stereotype.Component; /** * @author zhailiang * */ @Component public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; @Autowired private AuthenticationFailureHandler imoocAuthenticationFailureHandler; @Autowired private UserDetailsService userDetailsService; @Override public void configure(HttpSecurity http) throws Exception { SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter(); smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(imoocAuthenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler); SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider(); smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(smsCodeAuthenticationProvider) .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } }
Password
Authentication
Config
2. Authentication code refactoring - base class
2.1 ValidateCode
Contains validation code information: code, expireTime
2.2 ValidateCodeBeanConfig
Introduce SecurityProperties, ImageCodeGenerator, SmsCodeSender
2.3 ValidateCodeController
Introducing ValidateCodeProcessorHolder
Call createCode in createCode to create a verification code, returned by HttpServletResponse
2.4 ValidateCodeType
Enumeration classes, SMS, IMAGE
2.5 ValidateCodeSecurityConfig
Same 1.4
2.6 ValidateCodeProcessorHolder
Implement ValidateCodeProcessor's dependency lookup
2.7 ValidateCodeProcessor
Interface class, create and validate methods, ImageValidateCodeProcessor and SmCodeProcessor implement this interface
2.8 ValidateCodeGenerator
Interface class, containing generate methods, implemented by ImageValidateCodeGenerator and SmsValidateCodeGenerator
2.9 AbstractValidateCodeProcessor
- implements ValidateCodeProcessor
- Implement the basic flow of verification code: validate, create (save, send), generate
- ValidateCodeGenerator Injection Lookup
ImageValidateCodeGenerator and SmsValidateCodeGenerator - Genee finds the corresponding ImageValidateCodeGenerator or SmsValidateCodeGenerator in the validateCodeGenerators according to the request type.
/** * Collects the implementations of all {@link ValidateCodeGenerator} interfaces in the system. */ @Autowired private Map<String, ValidateCodeGenerator> validateCodeGenerators;
- create
Call generate to generate authentication code (graphics or text messages)
Call save to save in session
Call send to send a verification code (return ImageCode or send a text message) - validate
Remove the subclass of ValidateCode from session (ImageValidateCode or SmsValidateCode) and verify it with the validation code passed in from ServletWebRequest, which returns normally and captures him without throwing an exception (ValidateCodeException). - ImageCodeProcessor and SMCodeProcessor extend this class
2.10 ValidateCodeException
extends AuthenticationException
ImoocAuthentication FailureHandler will capture him
3. Graphic Authentication Code
3.1 ImageCode
extends ValidateCode, adds graphic validation code information: BufferedImage
3.2 ImageCodeGenerator
implements ValidateCodeGenerator
Genee Generates ImageCode
3.3 DemoImageCodeGenerator
implements ValidateCodeGenerator
Another generation generate s ImageCode
3.3 ImageCodeProcessor
extends AbstractValidateCodeProcessor
Overloaded send
@Override protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception { ImageIO.write(imageCode.getImage(), "JPEG", request.getResponse().getOutputStream()); }
4. Text Message Authentication Code
4.1 ValidateCode
Save SMS Authentication Codes directly using the underlying class ValidateCode, since there is no additional information, there is no need to extend subclasses
4.2 SmsCodeGenerator
- implements ValidateCodeGenerator
- Inject SecurityProperties and read the configuration information in application.properties, such as:
imooc.security.code.sms.length = 6 - Genee generate s ValidateCode
4.3 SmsCodeProcessor
extends AbstractValidateCodeProcessor
Overload send, call SmsCodeSender interface class, send SMS Authentication Code
4.4 SmsCodeSender
Interface class, containing send method
4.5 DefaultSmsCodeSender
implements SmsCodeSender, implements send method
4.6 SmsCodeAuthenticationToken
- Modified with UsernamePasswordAuthenticationToken
- extends AbstractAuthenticationToken
- private final Object principal;
Store mobile number
4.7 SmsCodeAuthenticationFilter
- Modified with UsernamePasswordAuthenticationFilter
- extends AbstractAuthenticationProcessingFilter
- attemptAuthentication is validation logic
Create SmsCodeAuthenticationToken with mobile number from HttpServletRequest
return this.getAuthenticationManager().authenticate(authRequest): Pass SmsCodeAuthenticationToken to AuthenticationManager, AuthenticationManager calls the chain of AuthenticationProvider, and finally this SmsCodeAuthenticationToken is processed by SmsCodeAuthenticationProvider and returns Authentication
/** * */ package com.wxm.spring.security.core.authentication.mobile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.util.Assert; import com.wxm.spring.security.core.properties.SecurityConstants; /** * @author zhailiang * */ public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // ~ Static fields/initializers // ===================================================================================== private String mobileParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE; private boolean postOnly = true; // ~ Constructors // =================================================================================================== public SmsCodeAuthenticationFilter() { //"/authentication/mobile" super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, "POST")); } // ~ Methods // ======================================================================================================== //Authentication logic 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(); SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile); // Allow subclasses to set the "details" property setDetails(request, authRequest); //Pass the SmsCodeAuthenticationToken to AuthenticationManager, which calls the chain of AuthenticationProvider s. //Finally, this SmsCodeAuthentication Token is processed by the SmsCodeAuthentication Provider and returned to Authentication return this.getAuthenticationManager().authenticate(authRequest); } /** * Get your mobile number */ protected String obtainMobile(HttpServletRequest request) { return request.getParameter(mobileParameter); } /** * Provided so that subclasses may configure what is put into the * authentication request's details property. * * @param request * that an authentication request is being created for * @param authRequest * the authentication request object that should have its details * set */ protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } /** * Sets the parameter name which will be used to obtain the username from * the login request. * * @param usernameParameter * the parameter name. Defaults to "username". */ public void setMobileParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.mobileParameter = usernameParameter; } /** * Defines whether only HTTP POST requests will be allowed by this filter. * If set to true, and an authentication request is received which is not a * POST request, an exception will be raised immediately and authentication * will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method * will be called as if handling a failed authentication. * <p> * Defaults to <tt>true</tt> but may be overridden by subclasses. */ public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getMobileParameter() { return mobileParameter; } }
4.8 SmsCodeAuthenticationProvider
- implements AuthenticationProvider
- supports: Returns whether a certain type of validation is supported
- authenticate: Verify logic, return Authentication
/** * */ package com.wxm.spring.security.core.authentication.mobile; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; /** * @author zhailiang * */ public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; /* * (non-Javadoc) * * @see org.springframework.security.authentication.AuthenticationProvider# * authenticate(org.springframework.security.core.Authentication) */ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication; UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); if (user == null) { throw new InternalAuthenticationServiceException("Unable to get user information"); } SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } /* * (non-Javadoc) * * @see org.springframework.security.authentication.AuthenticationProvider# * supports(java.lang.Class) */ @Override public boolean supports(Class<?> authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } }
4.9 SmsCodeAuthenticationSecurityConfig
- extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>
- Contains AuthenticationSuccessHandler, AuthenticationFailureHandler, userDetailsService
- Overloaded configure
Configure SmsCodeAuthenticationFilter
Configure SmsCodeAuthentication Provider
Insert smsCodeAuthenticationFilter before UsernamePasswordAuthenticationFilter
@Override public void configure(HttpSecurity http) throws Exception { SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter(); smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(imoocAuthenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler); SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider(); smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(smsCodeAuthenticationProvider) .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); }
4. Miscellaneous
4.1 Dependent Queries
@Autowired private Map<String, ValidateCodeProcessor> validateCodeProcessors;
4.2 Code Refactoring
BrowserSecurityConfig
SecurityConstants