spring-security (nineteen) core Filter-ExceptionTranslationFilter

Foreword:
  ExceptionTranslationFilter is in front of FilterSecurityInterceptor in spring's security filter chain. This filter itself does not perform specific security protection. It mainly handles various exceptions thrown by FilterSecurityInterceptor and returns to the client a suitable http response.
1. ExceptionTranslationFilter functions and properties
1. There are the following properties in the class
  • AuthenticationEntryPoint - handles logic that requires re-authentication
  • AccessDeniedHandler - Logic for handling denied requests
  • AuthenticationTrustResolver-judgment processing type
  • RequestCache - Caches client requests, and can continue to execute the original request that triggered the authentication after authentication is complete

2. The main logic of this filter is in the handleSpringSecurityException method, the steps are as follows
  • If the caught exception is AuthenticationException, re-authenticate
  • If the caught exception is AccessDeniedException, execute the following judgment further
  •          ■ If the current authentication form is Anonymous or RememberMe, re-execute the authentication
             ■ Otherwise, the current authenticated user does not have permission to access the requested resource, call the accessDeniedHandler.handle method

Let's take a look at the re-authentication method sendStartAuthentication
protected void sendStartAuthentication(HttpServletRequest request,
	HttpServletResponse response, FilterChain chain,
	AuthenticationException reason) throws ServletException, IOException {
	// SEC-112: Clear the SecurityContextHolder's Authentication, as the
	// existing Authentication is no longer considered valid
	SecurityContextHolder.getContext().setAuthentication(null);
	requestCache.saveRequest(request, response);
	logger.debug("Calling Authentication entry point.");
	authenticationEntryPoint.commence(request, response, reason);
}

First, the authentication object in the current SecurityContextHolder will be cleared, and then the current call will be saved in the requestCache, so that the current processing can be continued after the subsequent authentication is successful, and then the commence method of authenticationEntryPoint will be called to redirect the user to the authentication interface, prompting User authenticates.
3. AuthenticationEntryPoint class
The main function of this class is to present an appropriate response to the user to prompt the user to log in again. Common implementation classes include
  • BasicAuthenticationEntryPoint-EntryPoint corresponding to basic authentication, modify the header of Response, and return a 401 code to the user
  • CasAuthenticationEntryPoint-The corresponding EntryPoint for cas authentication, redirects the user to the login page of the Cas server
  • LoginUrlAuthenticationEntryPoint-form The EntryPoint corresponding to the login authentication, redirects to the login page
  • DelegatingAuthenticationEntryPoint - This EntryPoint is used when there are multiple EntryPoints in the system, and each EntryPoint corresponds to its own matching path. This class maintains a hash table of the correspondence between RequestMatcher and EntryPoint, finds the matching EntryPoint according to the incoming request and executes the corresponding commence method. There is also a defaultEntryPoint in this class. When all EntryPoints do not match the current request, the default EntryPoint is called. commence method.

Let's take LoginUrlAuthenticationEntryPoint as an example to see the code implementation
/**
 * Performs the redirect (or forward) to the login form URL.
*/
public void commence(HttpServletRequest request, HttpServletResponse response,
	AuthenticationException authException) throws IOException, ServletException {
	String redirectUrl = null;
	if (useForward) {
		if (forceHttps && "http".equals(request.getScheme())) {
			// First redirect the current request to HTTPS.
			// When that request is received, the forward to the login page will be used.
			redirectUrl = buildHttpsRedirectUrlForRequest(request);
		}
		if (redirectUrl == null) {
			String loginForm = determineUrlToUseForThisRequest(request, response,authException);
			if (logger.isDebugEnabled()) {
				logger.debug("Server side forward to: " + loginForm);
			}
			RequestDispatcher dispatcher =request.getRequestDispatcher(loginForm);
			dispatcher.forward(request, response);
			return;
		}
	}
	else {
		// redirect to login page. Use https if forceHttps true
		redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
	}
	redirectStrategy.sendRedirect(request, response, redirectUrl);
}

A brief description is as follows
  • When the configuration is in forward form, if the configured scheme must be https, and the currently initiated request is http, a redirect will be sent first, and just change http to https just like the current url, which will re-throw the authentication exception and restart the Entering this method, the request has become https at this time
  • Then get the loginFormUrl and directly forward to the corresponding interface
  • If the configuration is not to use forward, build a url that redirects to the login page, and then directly redirect to this interface

4.AccessDeniedHandler class
This class is mainly used to deal with the situation of access denial when the user is authenticated, and there is no permission to access the current resource. This situation should not happen in normal usage scenarios, because our application should only provide operational UI to users. For example, a page for admin access should be hidden from users who do not have admin privileges, but our security policy cannot only be Relying on the method of hiding links, because malicious users can directly enter the URL or bypass this restriction by modifying the parameters of the RESTful URL, an AccessDeniedException exception will occur in this case.
When an AccessDeniedException exception occurs, ExceptionTranslationFilter will entrust AccessDeniedHandler to handle it. By default, AccessDeniedHandlerImpl will be called and return a 403 code to the client. We can configure the errorpage property of AccessDeniedHandlerImpl to display a friendly page to the user (re- login page), you can also provide customized services by implementing AccessDeniedHandler yourself.
5.SavedRequest and RequestCache classes
Another function of the ExceptionTranslationFilter class is to save the current request before calling AuthenticationEntryPoint. By default, HttpSessionRequestCache is used to save it in the session, so that the current processing can be re-executed after the authentication is completed. For example, UsernamePasswordAuthenticationFilter and CasAuthenticationFilter call the onAuthenticationSuccess method of the SavedRequestAwareAuthenticationSuccessHandler class after successful authentication, use HttpSessionRequestCache to obtain the saved request information from the session, obtain the corresponding url, send a redirection to continue the previous request, and the RequestCacheAwareFilter class will then store the current request Replace with the saved request to restore the complete information of the previous request.
2. In the spring boot environment, how does this filter use the Java config mechanism to be added to the servlet to intercept our requests?
In the previous chapter (spring-security (16) Filter configuration principle) , we know that spring Security-related Filter is to call HttpSecurity's build in WebSecurity's build method to sort the filter list added to HttpSecurity and build it into a SecurityFilterChain, then append all SecurityFilterChains to FilterChainProxy, and finally register to ServletContext through DelegatingFilterProxy, Let's mainly look at how this class is added to the filter list of HttpSecuriy, and how the corresponding main properties are configured.
1. Starting from our configuration entry WebSecurityConfigurerAdapter class, in the getHttp() method of this class, the http.exceptionHandling() method will be called when the default configuration is
used. In this method, a configuration class ExceptionHandlingConfigurer that implements the SecurityConfigurer interface is created. By calling the getOrApply method, it is finally added to the configurers attribute of HttpSecurity. Through this configuration class, we can also set the authenticationEntryPoint, accessDeniedHandler and other attributes in the ExceptionTranslationFilter.
2. When WebSecurity builds HttpSecurity, it will call the build method of HttpSecurity. This method will first execute the configure() method of HttpSecurity, which is to call the configure method of each SecurityConfigurer in the configurers property in turn.
private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

private Collection<SecurityConfigurer<O, B>> getConfigurers() {
	List<SecurityConfigurer<O, B>> result = new ArrayList<SecurityConfigurer<O, B>>();
	for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
		result.addAll(configs);
	}
	return result;
}

3. Let's take a look at the configure method of ExceptionHandlingConfigurer
public void configure(H http) throws Exception {
	AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
	ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));
		if (accessDeniedHandler != null) {
			exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
		}
		exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
		http.addFilter(exceptionTranslationFilter);
	}

As you can see, a Filter will be created in this class and appended to the filter list of HttpSecurity.
In this way, we can clearly see that our ExceptionTranslationFilter has been added to the filter list of httpsecurity. Let's see how the main attributes of this filter are set. The
first is to create the getAuthenticationEntryPoint method of entryPoint.
AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
		AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
		if (entryPoint == null) {
			entryPoint = createDefaultEntryPoint(http);
		}
		return entryPoint;
	}

private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
	if (defaultEntryPointMappings.isEmpty()) {
		return new Http403ForbiddenEntryPoint();
	}
	if (defaultEntryPointMappings.size() == 1) {
		return defaultEntryPointMappings.values().iterator().next();
	}
	DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(defaultEntryPointMappings);
	entryPoint.setDefaultEntryPoint(defaultEntryPointMappings.values().iterator().next());
	return entryPoint;
}

Because we did not assign a value to this.authenticationEntryPoint in the default configuration, we will call createDefaultEntryPoint(H http) to get it. How is the defaultEntryPointMappings set in this place? In order to explain this property, we need to look at some additional configuration classes.
For example, when we enable the formLogin function, through http.formLogin() configuration, a FormLoginConfigurer class will be added to the http configurers list, because the init method of each configure will be executed first in the build method of httpsecurity, let's take a look below The init method of FormLoginConfigurer is clear. The specific code is in the parent class AbstractAuthenticationFilterConfigurer of FormLoginConfigurer.
public void init(B http) throws Exception {
		updateAuthenticationDefaults();
		if (permitAll) {
			PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
		}

		registerDefaultAuthenticationEntryPoint(http);
	}
private void registerDefaultAuthenticationEntryPoint(B http) {
	ExceptionHandlingConfigurer<B> exceptionHandling = http
			.getConfigurer(ExceptionHandlingConfigurer.class);
	if (exceptionHandling == null) {
		return;
	}
	ContentNegotiationStrategy contentNegotiationStrategy = http
			.getSharedObject(ContentNegotiationStrategy.class);
	if (contentNegotiationStrategy == null) {
		contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
	}

	MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(
			contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML,
			new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
	mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
	RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
				new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));

	RequestMatcher preferredMatcher = new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));

	exceptionHandling.defaultAuthenticationEntryPointFor(
				postProcess(authenticationEntryPoint), preferredMatcher);
	}

In this way, you can clearly see that by obtaining the ExceptionHandlingConfigurer object and calling the defaultAuthenticationEntryPointFor method, you can append the entrypoint corresponding to formLogin to the ExceptionHandlingConfigurer configuration class, and set the matching rules. The similar HttpBasicConfigurer also adds the entrypoint corresponding to the basic authentication in this form. When using cas authentication, spring does not provide us with a similar configuration class, but we can directly call the following method to set
http.exceptionHandling().authenticationEntryPoint(casEntryPoint())

The settings of the other two attributes, accessDeniedHandler and RequestCache, are relatively simple.
If accessDeniedHandler is specified in the configuration class, it is used. Otherwise, it is not set. The ExceptionTranslationFilter class uses the AccessDeniedHandlerImpl implementation class by default.
private RequestCache getRequestCache(H http) {
		RequestCache result = http.getSharedObject(RequestCache.class);
		if (result != null) {
			return result;
		}
		return new HttpSessionRequestCache();
	}

If specified, use the specified, otherwise use HttpSessionRequestCache by default.

In this way, our ExceptionTranslationFilter is completely assembled, and it is also added to the servlet as a Filter, which can handle exceptions during the authentication process.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326128988&siteId=291194637