Spring Security Learning note 2

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.

Keywords: Spring Session JSON

Added by p_h_p on Fri, 25 Oct 2019 13:03:19 +0300