Principle analysis of successful and failed spring security authentication processor
First of all, we need to know that the default success and failure processors of spring security are SavedRequestAwareAuthenticationSuccessHandler and simpleurauthenticationfailurehandler. Normally, when requesting a resource, the authentication success will jump to the resource to be requested only after entering the authentication of spring security.
Source code analysis of SavedRequestAwareAuthenticationSuccessHandler
Authentication success processor process: when a user requests an interface, it will be blocked by spring security. When the user authentication is successful, it will jump to the interface that the user wants to request.
Question: how do you know the user's request?
SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response). This method can cache the user's request.
Assuming that the user has successfully authenticated at this time, the call flow of the authentication successful processor is as follows:
AbstractAuthenticationProcessingFilter#successfulAuthentication>AuthenticationSuccessHandler#onAuthenticationSuccess>SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess
SavedRequestAwareAuthenticationSuccessHandler
@Override //This method is rewritten, which is different from the implementation in the parent class. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { //Cache the user's original request into SavedRequest SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest == null) { super.onAuthenticationSuccess(request, response, authentication); return; } String targetUrlParameter = getTargetUrlParameter(); if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request .getParameter(targetUrlParameter)))) { requestCache.removeRequest(request, response); super.onAuthenticationSuccess(request, response, authentication); return; } //Delete temporary authentication related data that may have been stored on the server clearAuthenticationAttributes(request); String targetUrl = savedRequest.getRedirectUrl(); logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl); //Jump to the specified original user request path getRedirectStrategy().sendRedirect(request, response, targetUrl); }
getRedirectStrategy() here is DefaultRedirectStrategy.
DefaultRedirectStrategy
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException { //Processing url String redirectUrl = calculateRedirectUrl(request.getContextPath(), url); redirectUrl = response.encodeRedirectURL(redirectUrl); if (logger.isDebugEnabled()) { logger.debug("Redirecting to '" + redirectUrl + "'"); } //Jump here response.sendRedirect(redirectUrl); }
Source code analysis of SavedRequestAwareAuthenticationSuccessHandler
When the onAuthenticationFailure method is called, the AuthenticationFailureHandler will jump based on the path of the defaultFailureUrl. If the defaultFailureUrl is not set, a 401 error code and an AuthenticationException related error message are sent to the client.
If the useForward property is set, RequestDispatcher.forware will be redirected to the destination instead of jumping.
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { //If the default error jump path is empty if (defaultFailureUrl == null) { logger.debug("No failure URL set, sending 401 Unauthorized error"); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage()); } else { //Save exception information saveException(request, exception); //If the forwardToDestination property value is true if (forwardToDestination) { logger.debug("Forwarding to " + defaultFailureUrl); request.getRequestDispatcher(defaultFailureUrl) .forward(request, response); } else { logger.debug("Redirecting to " + defaultFailureUrl); redirectStrategy.sendRedirect(request, response, defaultFailureUrl); } } } //According to the value of forwardToDestination, the error information is selectively saved in the request [redirection shares a request domain with the original request] or session. protected final void saveException(HttpServletRequest request, AuthenticationException exception) { if (forwardToDestination) { request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); } else { HttpSession session = request.getSession(false); if (session != null || allowSessionCreation) { request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); } } }
Custom success and failure processors
Successful processor
@Component @Log public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperties securityProperties; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info("Login success processor"); //In this way, the method calling the parent class will jump to the specified url after success. // super.onAuthenticationSuccess(request, response, authentication); //The following way is to print the successful user information to the console response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(authentication)); } } }
Failed processor
@Component @Log public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { //What spring MVC injects when loading JSON parsing tools can be used directly @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperties securityProperties; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("Login failure processor"); //In this way, the method of calling the parent class is to jump after failure. // super.onAuthenticationFailure(request, response, exception); //The following way is to print the error message of authentication failure to the console response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(exception)); } } }
Add a custom authentication processor to spring security
@Configuration @EnableWebSecurity public class WebMvcSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationFailureHandler failureHandler; @Autowired private MyAuthenticationSuccessHandler successHandler; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .successHandler(successHandler) .failureHandler(failureHandler) .and().authorizeRequests().anyRequest().authenticated(); } }
In this way, we can go to our own authentication processor in the authentication process.
If there is any mistake, please point out thank you.