You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
168
+
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.
169
+
It is typically not necessary to know the ordering of Spring Security's ``Filter``s.
170
+
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].
171
+
172
+
To exemplify the above paragraph, let's consider the following security configuration:
173
+
174
+
====
175
+
.Java
176
+
[source,java,role="primary"]
177
+
----
178
+
@Configuration
179
+
@EnableWebSecurity
180
+
public class SecurityConfig {
181
+
182
+
@Bean
183
+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
1. First, the `CsrfFilter` is invoked to protect against xref:servlet/exploits/csrf.adoc[CSRF attacks].
234
+
2. Second, the authentication filters are invoked to authenticate the request.
235
+
3. Third, the `AuthorizationFilter` is invoked to authorize the request.
236
+
237
+
[NOTE]
238
+
====
239
+
There might be other `Filter` instances that are not listed above.
240
+
If you want to see the list of filters invoked for a particular request, you can <<servlet-print-filters,print them>>.
241
+
====
242
+
243
+
[[servlet-print-filters]]
244
+
=== Printing the Security Filters
245
+
246
+
Often times, it is useful to see the list of security ``Filter``s that are invoked for a particular request.
247
+
For example, you want to make sure that the <<adding-custom-filter,filter you have added>> is in the list of the security filters.
248
+
249
+
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:
250
+
251
+
[source,text,role="terminal"]
252
+
----
253
+
2023-06-14T08:55:22.321-03:00 INFO 76975 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [
And that will give a pretty good idea of the security filters that are configured for <<servlet-securityfilterchain,each filter chain>>.
272
+
273
+
But that is not all, you can also configure your application to print the invocation of each individual filter for each request.
274
+
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.
275
+
To do that, you can configure your application to <<servlet-logging,log the security events>>.
276
+
277
+
[[adding-custom-filter]]
278
+
=== Adding a Custom Filter to the Filter Chain
279
+
280
+
Mostly of the times, the default security filters are enough to provide security to your application.
281
+
However, there might be times that you want to add a custom `Filter` to the security filter chain.
282
+
283
+
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.
284
+
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.
throw new AccessDeniedException("Access denied"); <4>
316
+
}
317
+
318
+
}
319
+
320
+
----
321
+
322
+
The sample code above does the following:
323
+
324
+
<1> Get the tenant id from the request header.
325
+
<2> Check if the current user has access to the tenant id.
326
+
<3> If the user has access, then invoke the rest of the filters in the chain.
327
+
<4> If the user does not have access, then throw an `AccessDeniedException`.
328
+
329
+
[TIP]
330
+
====
331
+
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.
332
+
====
333
+
334
+
Now, we need to add the filter to the security filter chain.
<1> Use `HttpSecurity#addFilterBefore` to add the `TenantFilter` before the `AuthorizationFilter`.
362
+
363
+
By adding the filter before the `AuthorizationFilter` we are making sure that the `TenantFilter` is invoked after the authentication filters.
364
+
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.
365
+
366
+
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.
367
+
368
+
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].
369
+
That may cause the filter to be invoked twice, once by the container and once by Spring Security and in a different order.
370
+
371
+
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`:
372
+
373
+
[source,java]
374
+
----
375
+
@Bean
376
+
public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {
377
+
FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter);
378
+
registration.setEnabled(false);
379
+
return registration;
380
+
}
381
+
----
382
+
205
383
206
384
[[servlet-exceptiontranslationfilter]]
207
385
== Handling Security Exceptions
@@ -333,3 +511,52 @@ XML::
333
511
=== RequestCacheAwareFilter
334
512
335
513
The {security-api-url}org/springframework/security/web/savedrequest/RequestCacheAwareFilter.html[`RequestCacheAwareFilter`] uses the <<requestcache,`RequestCache`>> to save the `HttpServletRequest`.
514
+
515
+
[[servlet-logging]]
516
+
== Logging
517
+
518
+
Spring Security provides comprehensive logging of all security related events at the DEBUG and TRACE level.
519
+
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.
520
+
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.
521
+
522
+
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.
523
+
With no logs, the user will see a 403 error with no explanation of why the request was rejected.
524
+
However, if you enable logging for Spring Security, you will see a log message like this:
525
+
526
+
[source,text]
527
+
----
528
+
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.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:8080/hello
535
+
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
536
+
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]
537
+
----
538
+
539
+
It becomes clear that the CSRF token is missing and that is why the request is being denied.
540
+
541
+
To configure your application to log all the security events, you can add the following to your application:
0 commit comments