Spring Security - Filter Chain
Created: 2020-07-03 | 5 min
After playing with few basic configurations of Spring Security, I noticed there is something important to understand before moving to check other features in the framework, which is its Filter Chain. This time I'll take a look at how Spring Security acts on the requests coming from the client, which involves a set of Filters, each of them with a very specific goal.
Note: This is not a tutorial, this is just some notes of what I understand (as of today) about Spring Security and its Filter Chain.
The most complete explanation I could find regarding how the Security Filter Chain is built in the entire architecture is in the official documentation in here: https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture
I'll write down what I interpret from what I already interacted with the module in combination with what the documentation is trying to explain.
There are five pieces (in the documentation I pointed out in the previous section) that I want to visit here. They are:
First of all, "Filters" (in the Servlet's world) came from the
Java Specification Requests (JSR) #53. Look at this:
- Ref: https://jcp.org/en/jsr/detail?id=53
- Direct link to the PDF (See Page #43): https://download.oracle.com/otn-pub/jcp/7840-servlet-2.3-spec-oth-JSpec/servlet-2_3-fcs-spec.pdf?AuthParam=1593651764_bd0a8306800ffcc92df386c00a6c89f0
Filters are some pieces of code that do something before or after a Request gets processed by a Servlet. The web container (e.g. Tomcat) will know that.
Filters have multiple purposes, it is not only restricted to be for Security mechanisms. They are meant to read or write (any logic in general) with the Request. For example: one filter can print some audit logs, another one can add some headers to the request, and so on.
In the Spring Security's world, we have a set of Filters strategically ordered to prevent subsequent filters or the controller itself being called if they find the request not matching with the security configuration.
The most straightforward example may be, imagine a user entering a wrong password, there is one filter dedicated to verify whether the password is correct or not. If not, then the at some point of the Filter Chain, one of the Filters will reject the request and respond with a 401 status code to the client.
DelegatingFilterProxy is a class registered as Standard Filter in the Container, but the container doesn't know anything about Spring Beans at this point.
DelegatingFilterProxy is the support to establish communication between the Standard Filters and the Spring Application Context.
Notice that I didn't mention anything about Spring Security yet, that's because
DelegatingFilterProxy is a class located in Spring Web and not Spring Security. Which is interesting because from there I interpret that this proxy can be used by something else not necessarily related to Spring Security.
FilterChainProxy is a filter located in Spring Security module.
It takes a list of filters and creates something called
VirtualFilterChain (a private class within
FilterChainProxy), which is going to take the list of the Security Filters and start the chain.
I want to point this out that seems to be pretty useful, quoting Spring Security docs:
First, it provides a starting point for all of Spring Security’s Servlet support. For that reason, if you are attempting to troubleshoot Spring Security’s Servlet support, adding a debug point in FilterChainProxy is a great place to start.
SecurityFilterChain contains the list of all the filters involved in Spring Security. This interface expose a method
List<Filter> getFilters() that returns all the filters such as the
In Spring Security, one or more
SecurityFilterChains can be registered in the
The following picture illustrates all the components I tried to explain so far:
SecurityFilterChain, by Spring Security Docs
Finally, at the end of all this hierarchy (Standard Filter > DelegatingFilterProxy > FilterChainProxy > SecurityFilterChain), there is list of possible security filters that we can configure in our applications. I'll just mention the ones that I kind of interated so far:
In fact, everytime your Spring Boot application starts up, the list of filters are printed in the logs by default (in their corresponding order of execution). Example:
// Raw logs at startup time // List of filters are printed there 2020-07-02 21:34:55.896 INFO 51095 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7fe82967, org.springframework.security.web.context.SecurityContextPersistenceFilter@9596ce8, org.springframework.security.web.header.HeaderWriterFilter@26f46fa6, org.springframework.security.web.csrf.CsrfFilter@558756be, org.springframework.security.web.authentication.logout.LogoutFilter@80bfdc6, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6edcad64, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@75ae4a1f, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@476fe690, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@50850539, org.springframework.security.web.session.SessionManagementFilter@227a47, org.springframework.security.web.access.ExceptionTranslationFilter@3968bc60, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@2924f1d8] // And this is the list of filters in a more readable way: [ 1. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@5fa23c 2. org.springframework.security.web.context.SecurityContextPersistenceFilter@73c9e8e8 3. org.springframework.security.web.header.HeaderWriterFilter@59f93db8 4. org.springframework.security.web.csrf.CsrfFilter@13d9261f 5. org.springframework.security.web.authentication.logout.LogoutFilter@b606cb6 6. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@2a3194c6 7. org.springframework.security.web.savedrequest.RequestCacheAwareFilter@de8039f 8. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4b790d86 9. org.springframework.security.web.authentication.AnonymousAuthenticationFilter@558756be 10. org.springframework.security.web.session.SessionManagementFilter@1aabf50d 11. org.springframework.security.web.access.ExceptionTranslationFilter@2e8a1ab4 12. org.springframework.security.web.access.intercept.FilterSecurityInterceptor@51c959a4 ]
So, any requests coming from the client will pass through the filter chain and in that order.
- I started to write this article thinking of the Filter Chain as something strictily related to Spring Security. BUT, during all my reading I got that this part of the architecture in regards of how Spring Security Filters are added into the actual "standard" filter chain as something that you first need to know other "external" concepts about Filters, Servlets and the interfaces that Spring Web provides. Only then, all the illustrations and explanation in the official docs started to make sense to me.
- Debugging one of my few simple examples during startup time helped me a lot to understand some pieces, specifically having some breakpoints in
FilterChainProxythen going all the way down and all the way up in the whole call hierarchy was pretty useful. Still, there are A LOT of areas in the codebase of Spring Security and Spring Web that I don't understand right now..
- I'm impressed the quality of the docstrings Spring has in its codebase, I found a lot of insights there, like examples, code snippets, explanation in details (more than I could find in the official docs).
- This is where most part of this post comes from: https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture
- Architecture Deep Dive in Spring Security - Joe Grandja @ Spring I/O 2017: https://www.youtube.com/watch?v=8rnOsF3RVQc
- SpringOne 2GX 2012 - Getting Started with Spring Security 3.1 - Rob Winch: https://www.youtube.com/watch?v=k32KqrckLEE