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
@@ -164,11 +164,13 @@ In fact, a `SecurityFilterChain` might have zero security `Filter` instances if
164
164
== Security Filters
165
165
166
166
The Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API.
167
-
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.
167
+
Those filters can be used for a number of different purposes, like
168
+
xref:servlet/exploits/index.adoc[exploit protection],xref:servlet/authentication/index.adoc[authentication], xref:servlet/authorization/index.adoc[authorization], and more.
168
169
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
170
It is typically not necessary to know the ordering of Spring Security's ``Filter``s.
170
171
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
173
+
These security filters are most often declared using an javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[`HttpSecurity`] instance.
172
174
To exemplify the above paragraph, let's consider the following security configuration:
173
175
174
176
[tabs]
@@ -185,11 +187,12 @@ public class SecurityConfig {
185
187
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
186
188
http
187
189
.csrf(Customizer.withDefaults())
190
+
.httpBasic(Customizer.withDefaults())
191
+
.formLogin(Customizer.withDefaults())
188
192
.authorizeHttpRequests(authorize -> authorize
189
193
.anyRequest().authenticated()
190
-
)
191
-
.httpBasic(Customizer.withDefaults())
192
-
.formLogin(Customizer.withDefaults());
194
+
);
195
+
193
196
return http.build();
194
197
}
195
198
@@ -210,11 +213,11 @@ class SecurityConfig {
210
213
fun filterChain(http: HttpSecurity): SecurityFilterChain {
211
214
http {
212
215
csrf { }
216
+
httpBasic { }
217
+
formLogin { }
213
218
authorizeHttpRequests {
214
219
authorize(anyRequest, authenticated)
215
220
}
216
-
httpBasic { }
217
-
formLogin { }
218
221
}
219
222
return http.build()
220
223
}
@@ -235,8 +238,8 @@ The above configuration will result in the following `Filter` ordering:
235
238
|====
236
239
237
240
1. First, the `CsrfFilter` is invoked to protect against xref:servlet/exploits/csrf.adoc[CSRF attacks].
238
-
2. Second, the authentication filters are invoked to authenticate the request.
239
-
3. Third, the `AuthorizationFilter` is invoked to authorize the request.
241
+
2. Second, xref:servlet/authentication/architecture.adoc[the authentication filters] are invoked to authenticate the request.
242
+
3. Third, xref:servlet/authorization/authorize-http-requests.adoc[the `AuthorizationFilter`] is invoked to authorize the request.
240
243
241
244
[NOTE]
242
245
====
@@ -254,22 +257,7 @@ The list of filters is printed at DEBUG level on the application startup, so you
254
257
255
258
[source,text,role="terminal"]
256
259
----
257
-
2023-06-14T08:55:22.321-03:00 DEBUG 76975 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [
2023-06-14T08:55:22.321-03:00 DEBUG 76975 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [ DisableEncodeUrlFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter]
273
261
----
274
262
275
263
And that will give a pretty good idea of the security filters that are configured for <<servlet-securityfilterchain,each filter chain>>.
@@ -279,13 +267,52 @@ That is helpful to see if the filter you have added is invoked for a particular
279
267
To do that, you can configure your application to <<servlet-logging,log the security events>>.
280
268
281
269
[[adding-custom-filter]]
282
-
=== Adding a Custom Filter to the Filter Chain
270
+
=== Adding Filters to the Filter Chain
271
+
272
+
Most of the time, the default <<servlet-security-filters>> are enough to provide security to your application.
273
+
However, there might be times that you want to add a custom `Filter` to the <<servlet-securityfilterchain>>.
274
+
275
+
javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[] comes with three methods for adding filters:
276
+
277
+
* `#addFilterBefore(Filter, Class<?>)` adds your filter before another filter
278
+
* `#addFilterAfter(Filter, Class<?>)` adds your filter after another filter
279
+
* `#addFilterAt(Filter, Class<?>)` replaces another filter with your filter
280
+
281
+
==== Adding a Custom Filter
282
+
283
+
If you are creating a filter of your own, you will need to determine its location in the filter chain.
284
+
Please take a look at the following key events that occur in the filter chain:
285
+
286
+
1. xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContext`] is loaded from the session
287
+
2. Request is protected from common exploits; xref:features/exploits/headers.adoc[secure headers], xref:servlet/integrations/cors.adoc[CORS], xref:servlet/exploits/csrf.adoc[CSRF]
288
+
3. Request is xref:servlet/authentication/architecture.adoc[authenticated]
289
+
4. Request is xref:servlet/authorization/architecture.adoc[authorized]
290
+
291
+
Consider which events you need to have happened in order to locate your filter.
292
+
The following is a rule of thumb:
293
+
294
+
[cols="1,1,1"]
295
+
|===
296
+
| If your filter is a(n) | Then place it after | As these events have already occurred
283
297
284
-
Most of the time, the default security filters are enough to provide security to your application.
285
-
However, there might be times that you want to add a custom `Filter` to the security filter chain.
298
+
| exploit protection filter
299
+
| SecurityContextHolderFilter
300
+
| 1
301
+
302
+
| authentication filter
303
+
| LogoutFilter
304
+
| 1, 2
305
+
306
+
| authorization filter
307
+
| AnonymousAuthenticationFilter
308
+
| 1, 2, 3
309
+
|===
310
+
311
+
[TIP]
312
+
Most commonly, applications add a custom authentication.
313
+
This means they should be placed after xref:servlet/authentication/logout.adoc[`LogoutFilter`].
286
314
287
315
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.
288
-
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.
289
316
290
317
First, let's create the `Filter`:
291
318
@@ -335,7 +362,11 @@ The sample code above does the following:
335
362
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.
336
363
====
337
364
338
-
Now, we need to add the filter to the security filter chain.
365
+
Now, you need to add the filter to the <<servlet-securityfilterchain>>.
366
+
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.
367
+
368
+
Based on the rule of thumb, add it after xref:servlet/authentication/anonymous.adoc[ `AnonymousAuthenticationFilter`], the last authentication filter in the chain, like so:
<1> Use `HttpSecurity#addFilterBefore` to add the `TenantFilter` before the `AuthorizationFilter`.
399
+
<1> Use `HttpSecurity#addFilterAfter` to add the `TenantFilter` after the `AnonymousAuthenticationFilter`.
369
400
370
-
By adding the filter before the `AuthorizationFilter` we are making sure that the `TenantFilter` is invoked after the authentication filters.
371
-
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.
401
+
By adding the filter after the xref:servlet/authentication/anonymous.adoc[`AnonymousAuthenticationFilter`] we are making sure that the `TenantFilter` is invoked after the authentication filters.
372
402
373
403
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.
374
404
375
-
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}reference/web/servlet.html#web.servlet.embedded-container.servlets-filters-listeners.beans[register it with the embedded container].
405
+
==== Declaring Your Filter as a Bean
406
+
407
+
When you declare a `Filter` as a Spring bean, either by annotating it with `@Component` or by declaring it as a bean in your configuration, Spring Boot automatically {spring-boot-reference-url}reference/web/servlet.html#web.servlet.embedded-container.servlets-filters-listeners.beans[registers it with the embedded container].
376
408
That may cause the filter to be invoked twice, once by the container and once by Spring Security and in a different order.
377
409
378
-
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`:
410
+
Because of that, filters are often not Spring beans.
411
+
412
+
However, if your filter needs to be a Spring bean (to take advantage of dependency injection, for example) you can tell Spring Boot to not register it with the container by declaring a `FilterRegistrationBean` bean and setting its `enabled` property to `false`:
379
413
380
414
[source,java]
381
415
----
@@ -387,6 +421,141 @@ public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilte
387
421
}
388
422
----
389
423
424
+
This makes so that `HttpSecurity` is the only one adding it.
425
+
426
+
==== Customizing a Spring Security Filter
427
+
428
+
Generally, you can use a filter's DSL method to configure Spring Security's filters.
429
+
For example, the simplest way to add `BasicAuthenticationFilter` is by asking the DSL to do it:
In this case, remove the call to `httpBasic` since you are constructing `BasicAuthenticationFilter` yourself.
549
+
550
+
[TIP]
551
+
====
552
+
In the event that you are unable to reconfigure `HttpSecurity` to not add a certain filter, you can typically disable the Spring Security filter by calling its DSL's `disable` method like so:
0 commit comments