preface
In the process of using the Spring Security framework, there is often a need to add additional data, such as authentication code, user type, etc. during login authentication. The following describes how to implement it.
Note: my project is based on the Spring Boot framework. If it is configured in xml, please study it by yourself.
Implement custom WebAuthenticationDetails
This class provides the function of obtaining additional information carried by users when logging in. By default, WebAuthenticationDetails provides remoteAddress and sessionId information. Developers can get WebAuthenticationDetails through getDetails() of Authentication. We write a custom class CustomWebAuthenticationDetails, which inherits from WebAuthenticationDetails and adds the data we care about (the following is a token field).
For this demand, there are many online solutions, such as using filter and custom Provider
My method is to add an AuthenticationProvider. The specific implementation is as follows:
1. Add WebAuthenticationDetails implementation class to save verification code information
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails { private String imageCode; private String session_imageCode; private long session_imageTime; public CustomWebAuthenticationDetails(HttpServletRequest request) { super(request); this.imageCode = request.getParameter("imageCode"); this.session_imageCode = (String)request.getSession().getAttribute("session_imageCode"); String session_verifyTime = (String)request.getSession().getAttribute("session_imageTime"); if(session_verifyTime == null) { this.session_imageTime= 0L; } else { this.session_imageTime= Long.parseLong(session_verifyTime); } } public String getImageCode(){ return imageCode; } public String getSession_imageCode() { return session_imageCode; } public long getSession_imageTime() { return session_imageTime; } }
Note: on the login page, you can put the token field in the form form, or directly add it to the parameters of the url, and then send additional data to the background.
2. Add AuthenticationDetailsSource implementation class
This interface is used to fill in the details of the user's login information during Spring Security login. The default implementation is WebAuthenticationDetailsSource, and the above default implementation WebAuthenticationDetails is generated. We write a class to implement AuthenticationDetailsSource, which is used to generate CustomWebAuthenticationDetails customized above.
@Component public class CustomAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> { @Override public WebAuthenticationDetails buildDetails(HttpServletRequest context) { return new CustomWebAuthenticationDetails(context); } }
3. Customize the AuthenticationProvider implementation class and add it to the authentication list
AuthenticationProvider provides login authentication processing logic. We implement this interface and write our own authentication logic.
@Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) authentication.getDetails(); String imageCode = details.getImageCode(); String session_imageCode = details.getSession_imageCode(); long session_imageTime = details.getSession_imageTime(); if(imageCode == null || session_imageCode == null) { throw new ImageCodeIllegalException("Verification code error"); } if(!imageCode.equals(session_imageCode)) { throw new ImageCodeIllegalException("Verification code error"); }else{ long nowTime = System.currentTimeMillis(); if((nowTime - session_imageTime)/1000 > 60) { //More than 60s, timeout throw new ImageCodeIllegalException("Verification code timed out"); } } return null; //If you want to have a password verification provider in the future, you need to return null directly here } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } }
4. Add configuration in the implementation class of WebSecurityConfigurerAdapter
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Inject private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource; @Inject private AuthenticationProvider customAuthenticationProvider; @Inject public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthenticationProvider);//a key } @Override protected void configure(HttpSecurity http) throws Exception { ...... .and() .formLogin() .loginProcessingUrl("/api/login") .usernameParameter("username") .passwordParameter("password") .authenticationDetailsSource(authenticationDetailsSource) //a key .permitAll() ...... } }
In order to achieve the effect of "burn after use", the operation of clearing the verification code is added to the subsequent processing classes of login success and failure
@Component public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @Autowired private PasswordService passwordService; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //Remove verification code request.getSession().removeAttribute("session_verifyObj"); request.getSession().removeAttribute("session_verifyObjTime"); response.setStatus(HttpServletResponse.SC_OK); } }
@Component public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Autowired private PasswordService passwordService; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { //Remove verification code request.getSession().removeAttribute("session_imgeCode"); request.getSession().removeAttribute("session_imageTime"); ...... response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed"); } }
Then add relevant configurations in the implementation class of WebSecurityConfigurerAdapter
...... .and() .formLogin() .loginProcessingUrl("/api/login") .successHandler(ajaxAuthenticationSuccessHandler) //a key .failureHandler(ajaxAuthenticationFailureHandler) //a key .usernameParameter("username") .passwordParameter("password") .authenticationDetailsSource(authenticationDetailsSource) .permitAll() ......