| 
					
				 | 
			
			
				@@ -167,44 +167,222 @@ In fact, a `SecurityFilterChain` might have zero security `Filter` instances if 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 == Security Filters 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 The Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-The <<servlet-filters-review,order of `Filter`>> instances matters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-It is typically not necessary to know the ordering of Spring Security's `Filter` instances. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-However, there are times that it is beneficial to know the ordering. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-The following is a comprehensive list of Spring Security Filter ordering: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* xref:servlet/authentication/session-management.adoc#session-mgmt-force-session-creation[`ForceEagerSessionCreationFilter`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `ChannelProcessingFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `WebAsyncManagerIntegrationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `SecurityContextPersistenceFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `HeaderWriterFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `CorsFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `CsrfFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `LogoutFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `OAuth2AuthorizationRequestRedirectFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `Saml2WebSsoAuthenticationRequestFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `X509AuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `AbstractPreAuthenticatedProcessingFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `CasAuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `OAuth2LoginAuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `Saml2WebSsoAuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `DefaultLoginPageGeneratingFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `DefaultLogoutPageGeneratingFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `ConcurrentSessionFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `BearerTokenAuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* <<requestcacheawarefilter,RequestCacheAwareFilter>> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `SecurityContextHolderAwareRequestFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `JaasApiIntegrationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `RememberMeAuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `AnonymousAuthenticationFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `OAuth2AuthorizationCodeGrantFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `SessionManagementFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-* `SwitchUserFilter` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Those filters can be used for a number of different purposes, like xref:servlet/authentication/index.adoc[authentication], xref:servlet/authorization/index.adoc[authorization], xref:servlet/exploits/index.adoc[exploit protection], and more. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The filters are executed in a specific order to guarantee that they are invoked at the right time, for example, the `Filter` that performs authentication should be invoked before the `Filter` that performs authorization. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+It is typically not necessary to know the ordering of Spring Security's ``Filter``s. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+However, there are times that it is beneficial to know the ordering, if you want to know them, you can check the {gh-url}/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java[`FilterOrderRegistration` code]. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+To exemplify the above paragraph, let's consider the following security configuration: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.Java 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Configuration 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@EnableWebSecurity 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public class SecurityConfig { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .csrf(Customizer.withDefaults()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .authorizeHttpRequests(authorize -> authorize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .anyRequest().authenticated() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .httpBasic(Customizer.withDefaults()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .formLogin(Customizer.withDefaults()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return http.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.Kotlin 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,kotlin,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.config.web.servlet.invoke 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Configuration 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@EnableWebSecurity 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class SecurityConfig { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fun filterChain(http: HttpSecurity): SecurityFilterChain { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        http { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            csrf { } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            authorizeHttpRequests { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                authorize(anyRequest, authenticated) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            httpBasic { } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            formLogin { } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return http.build() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The above configuration will result in the following `Filter` ordering: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[cols="1,1", options="header"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+|==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+| Filter | Added by 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+| xref:servlet/exploits/csrf.adoc[CsrfFilter] | `HttpSecurity#csrf` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+| xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[UsernamePasswordAuthenticationFilter] | `HttpSecurity#formLogin` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+| xref:servlet/authentication/passwords/basic.adoc[BasicAuthenticationFilter] | `HttpSecurity#httpBasic` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+| xref:servlet/authorization/authorize-http-requests.adoc[AuthorizationFilter] | `HttpSecurity#authorizeHttpRequests` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+|==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+1. First, the `CsrfFilter` is invoked to protect against xref:servlet/exploits/csrf.adoc[CSRF attacks]. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2. Second, the authentication filters are invoked to authenticate the request. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+3. Third, the `AuthorizationFilter` is invoked to authorize the request. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[NOTE] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+There might be other `Filter` instances that are not listed above. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+If you want to see the list of filters invoked for a particular request, you can <<servlet-print-filters,print them>>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[[servlet-print-filters]] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+=== Printing the Security Filters 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Often times, it is useful to see the list of security ``Filter``s that are invoked for a particular request. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+For example, you want to make sure that the <<adding-custom-filter,filter you have added>> is in the list of the security filters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The list of filters is printed at INFO level on the application startup, so you can see something like the following on the console output for example: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,text,role="terminal"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T08:55:22.321-03:00  INFO 76975 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.session.DisableEncodeUrlFilter@404db674, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@50f097b5, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.context.SecurityContextHolderFilter@6fc6deb7, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.header.HeaderWriterFilter@6f76c2cc, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.csrf.CsrfFilter@c29fe36, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.authentication.logout.LogoutFilter@ef60710, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c2dfa2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4397a639, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7add838c, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5cc9d3d0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7da39774, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@32b0876c, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3662bdff, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.access.ExceptionTranslationFilter@77681ce4, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+org.springframework.security.web.access.intercept.AuthorizationFilter@169268a7] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+And that will give a pretty good idea of the security filters that are configured for <<servlet-securityfilterchain,each filter chain>>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+But that is not all, you can also configure your application to print the invocation of each individual filter for each request. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+That is helpful to see if the filter you have added is invoked for a particular request or to check where an exception is coming from. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+To do that, you can configure your application to <<servlet-logging,log the security events>>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[[adding-custom-filter]] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+=== Adding a Custom Filter to the Filter Chain 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Mostly of the times, the default security filters are enough to provide security to your application. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+However, there might be times that you want to add a custom `Filter` to the security filter chain. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+For example, let's say that you want to add a `Filter` that gets a tenant id header and check if the current user has access to that tenant. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The previous description already gives us a clue on where to add the filter, since we need to know the current user, we need to add it after the authentication filters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+First, let's create the `Filter`: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.io.IOException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.Filter; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.FilterChain; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.ServletException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.ServletRequest; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.ServletResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.http.HttpServletRequest; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jakarta.servlet.http.HttpServletResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.access.AccessDeniedException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public class TenantFilter implements Filter { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HttpServletRequest request = (HttpServletRequest) servletRequest; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HttpServletResponse response = (HttpServletResponse) servletResponse; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        String tenantId = request.getHeader("X-Tenant-Id"); <1> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        boolean hasAccess = isUserAllowed(tenantId); <2> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (hasAccess) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            filterChain.doFilter(request, response); <3> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        throw new AccessDeniedException("Access denied"); <4> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+The sample code above does the following: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<1> Get the tenant id from the request header. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<2> Check if the current user has access to the tenant id. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<3> If the user has access, then invoke the rest of the filters in the chain. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<4> If the user does not have access, then throw an `AccessDeniedException`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[TIP] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Instead of implementing `Filter`, you can extend from {spring-framework-api-url}org/springframework/web/filter/OncePerRequestFilter.html[OncePerRequestFilter] which is a base class for filters that are only invoked once per request and provides a `doFilterInternal` method with the `HttpServletRequest` and `HttpServletResponse` parameters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Now, we need to add the filter to the security filter chain. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.Java 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // ... 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .addFilterBefore(new TenantFilter(), AuthorizationFilter.class); <1> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return http.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.Kotlin 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,kotlin,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+fun filterChain(http: HttpSecurity): SecurityFilterChain { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    http 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // ... 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .addFilterBefore(TenantFilter(), AuthorizationFilter::class.java) <1> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return http.build() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<1> Use `HttpSecurity#addFilterBefore` to add the `TenantFilter` before the `AuthorizationFilter`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+By adding the filter before the `AuthorizationFilter` we are making sure that the `TenantFilter` is invoked after the authentication filters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+You can also use `HttpSecurity#addFilterAfter` to add the filter after a particular filter or `HttpSecurity#addFilterAt` to add the filter at a particular filter position in the filter chain. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+And that's it, now the `TenantFilter` will be invoked in the filter chain and will check if the current user has access to the tenant id. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Be careful when you declare your filter as a Spring bean, either by annotating it with `@Component` or by declaring it as a bean in your configuration, because Spring Boot will automatically {spring-boot-reference-url}web.html#web.servlet.embedded-container.servlets-filters-listeners.beans[register it with the embedded container]. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+That may cause the filter to be invoked twice, once by the container and once by Spring Security and in a different order. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+If you still want to declare your filter as a Spring bean to take advantage of dependency injection for example, and avoid the duplicate invocation, you can tell Spring Boot to not register it with the container by declaring a `FilterRegistrationBean` bean and setting its `enabled` property to `false`: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,java] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@Bean 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    registration.setEnabled(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return registration; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 [[servlet-exceptiontranslationfilter]] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 == Handling Security Exceptions 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -336,3 +514,52 @@ XML:: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 === RequestCacheAwareFilter 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 The {security-api-url}org/springframework/security/web/savedrequest/RequestCacheAwareFilter.html[`RequestCacheAwareFilter`] uses the <<requestcache,`RequestCache`>> to save the `HttpServletRequest`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[[servlet-logging]] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+== Logging 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Spring Security provides comprehensive logging of all security related events at the DEBUG and TRACE level. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+This can be very useful when debugging your application because for security measures Spring Security does not add any detail of why a request has been rejected to the response body. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+If you come across a 401 or 403 error, it is very likely that you will find a log message that will help you understand what is going on. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Let's consider an example where a user tries to make a `POST` request to a resource that has xref:servlet/exploits/csrf.adoc[CSRF protection] enabled without the CSRF token. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+With no logs, the user will see a 403 error with no explanation of why the request was rejected. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+However, if you enable logging for Spring Security, you will see a log message like this: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,text] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.797-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing POST /hello 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.797-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/15) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.798-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/15) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.800-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderFilter (3/15) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.801-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (4/15) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.802-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking CsrfFilter (5/15) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/hello 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.s.w.access.AccessDeniedHandlerImpl   : Responding with 403 status code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+2023-06-14T09:44:25.814-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+It becomes clear that the CSRF token is missing and that is why the request is being denied. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+To configure your application to log all the security events, you can add the following to your application: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.application.properties in Spring Boot 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,properties,role="primary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+logging.level.org.springframework.security=TRACE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.logback.xml 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+[source,xml,role="secondary"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<configuration> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <!-- ... --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </appender> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <!-- ... --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <logger name="org.springframework.security" level="trace" additivity="false"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <appender-ref ref="Console" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </logger> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</configuration> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+---- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+==== 
			 |