Demystifying Spring Security setup
The code samples used in this article can be found at the following link :
So the previous weekend, I reconnected with an old friend of mine and we found ourselves discussing the Spring framework and its support for application security. Throughout the conversation, I could feel a nagging question lingering at the back of my mind -
Do I really know how Spring wires up its security infrastructure? Or am I just bluffing?
Imposter Syndrome, one might say.
The weekend passed and I resumed my usual work but I could still feel the question looming at the back of my mind. So much so, that I spent an entire afternoon trying to piece together info on Spring security setup. But I soon realized that it will take a little more than an afternoon’s lazy googling to get answers to all my questions.
So after a good deal of research, here I present a recount of my findings and understanding of how Spring Framework (Spring Boot) Auto-configures Spring Security. I have included code samples and examples from the spring framework library to explain the concept better.
So first things first, spring-security works through filters. If you were to put a debugger in your rest controller methods, you’d see several servlet filters, ex — UsernamePasswordAuthenticationFilter, CsrfFilter, etc (the complete list can be found here) are stacked in a filter chain. Each filter executes its logic and then forwards the incoming request down the filter chain.
Now, you must be wondering -
- How do these individual filters know which filter to call next?
- How does Spring boot know which set of filters to invoke before passing on the request to the Rest Controllers?
- Are these sets of filters fixed?
- What happens when we have different authentication & authorization mechanism in place? Does the list of filters change?
- If we have multiple authentication mechanisms in place, does it mean that there will be multiple lists of filters? In that case, how does spring boot auto-configure the correct list of filters?
And so on …
Well, you are not alone. I too had the same doubts and therefore I decided to dig further.
To understand the setup better, let me add the filter setup diagram from Spring Security Documentation.
The important piece of information from the above picture is -
Let me break them down one by one.
- DelegatingFilterProxy — This is a servlet filter embedded in the spring context. Its job is to delegate the incoming request to a bunch of filters (not managed as spring beans) provided by the Spring web framework and hence the name, DelegatingFilterProxy. We will see later on how Spring Boot constructs this Filter with some code examples.
- FilterChainProxy — This is another layer of indirection provided by Spring security. This is also a servlet filter whose job is to invoke the relevant filters that would work on the incoming request. Like the DelegatingFilterProxy, it also does not perform any logic. This filter is however provided by the Spring security package.
- SecurityFilter(s) — This is the most interesting bit in the above chain. This class basically contains a list of actual filters that need to be invoked before the controller can handle the request. It contains filters for example — SecurityContextPersistenceFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter etc. when one uses the default security config. FilterChainProxy queries this class to invoke each filter in a loop.
There can be multiple SecurityFilterChain, each having its own stack of filters which could be the same or different from the other SecurityFilterChain.
But how are these filters being constructed?
And more importantly how is spring boot auto-configuring all these complex filters in the background?
To understand the auto-configuration of the entire setup, let me enhance the above diagram.
The text in bold — SecurityFilterAutoConfiguration, WebSecurityConfiguration and WebSecurityConfigurerAdapter are the java class names that are responsible for the creation of DelegatingFilterProxy, FilterChainProxy and SecurityFilterChain respectively.
The boxes colored yellow/red are the components that are used in the security filtering part of the request.
The box in green — DispatcherServlet is called once the request is validated by the filter chain.
Let us explore them one by one.
This proxy filter is created by spring boot in SecurityFilterAutoConfiguration class as follows:
You’d notice that Spring boot autoconfigures a Bean of the type DelegatingFilterProxy via a filter registration class -> DelegatingFilterProxyRegistrationBean class. The bean is only created if another bean named -> springSecurityFilterChain is found in the classpath.
springSecurityFilterChain is an alias for the Spring Security’s FilterChainProxy class. It means this filter is optional and only created if we use spring security in our application.
You’d also notice that while creating this DelegatingFilterProxy Filter, we pass the springSecurityFilterChain name in its constructor. It is done, to set the delegation link between the two classes. DelegatingFilterProxy uses this delegation link during runtime to invoke spring security functionality. A snippet of how it is done is shown below:
this.delegate refers to the FilterChainProxy Bean.
Now, let us see how the FilterChainProxy is configured by Spring Security.
FilterChainProxy is configured whenever we use the @EnableWebSecurity annotation in the security config. An example of it is shown below.
If you inspect the @EnableWebSecurity annotation further, you'd notice that it imports the class -> WebSecurityConfiguration, class. This is where all the magic is happening. The exact code block that is creating the FilterChainProxy is as follows:
In a nutshell, this method constructs a bean (FilterChainProxy) with the name springSecurityFilterChain. While doing so, it configures any SecurityFilterChains available. It also, applies a default WebSecurityConfigurerAdapter implementation ( more on it later), if it cannot find. After that, it scans the available web security customizers (used to customize the security filter chain config) and applies them to the instance of WebSecurity. In the final step, it invokes the build of the WebSecurity instance.
2 things that might be on your mind now -
1. What is the role of WebSecurityConfigurerAdapter?
2. And, what is this WebSecurity class and what function does it perform?
I'll provide an answer to these 2 questions in the next sub-section. But for now, we know-how and most importantly, when the FilterChainProxy is configured by Spring Boot.
If you inspect the FilterChainProxy class, you'd notice how it invokes the security filter chains and in turn trigger the stack of security filters. Here is a snippet of the same:
Please follow the comments in the above snippet, you'd get the flow easily.
Let's move on to the next section.
WebSecurityConfigurerAdapter, WebSecurity and HttpSecurity
Posed with the above 2 questions, you might be also wondering how is the SecurityFilterChain constructed. So, in this sub-section, we will explore the role of -
WebSecurity is the configuration class that determines the filters to be applied for a particular web request along with other web request config responsibility. It, in turn, uses, HttpSecurity to achieve path-level filtering.
HttpSecurity class defines the list of filters that will be configured. It contains the RequestMatcher instance to decide on filter applicability for a particular incoming request.
A WebSecurity instance can refer to multiple HttpSecurity instances. Whenever multiple HttpSecurity classes are configured, it ends up creating multiple SecurityFilterChains corresponding to each HttpSecurity instance.
So in essence, there is a 1:N relationship between WebSecurity and HttpSecurity instances.
Now, how can we configure the HttpSecurity instances?
This is where the class -> WebSecurityConfigurerAdapter comes to use. This class is used to configure/customize the HttpSecurity instance. We generally override the configure(HttpSecurity http) method of this class in our security configs (See above for attached code snippet).
So, if we have multiple implementations/multiple classes extending WebSecurityConfigurerAdapter’s configure method, then we are essentially creating multiple SecurityFilterChain.
To understand the above paragraph of information better and see how all of them fit into the FilterChainProxy filter, here is an image depicting the same:
During FilterChainProxy’s instantiation, you’d remember, that it was invoking the build() method on a particular WebSecurity instance. This build() method, in turn, invokes the HttpSecurity instance’s build() methods. As a result, all the WebSecurityConfigurerAdapter config code is applied to HttpSecurity instances and therefore we end up customizing the SecurityFilterChain.
Let me add the relevant snippets of code to demonstrate how it's playing out in the background. Please read the comments above the following methods to understand the flow of logic better.
The comments above the methods describe the flow of creating multiple security filter chains. From a consumption perspective, all we need to do is extend the WebSecurityConfigurationAdapter class and override the configure() method.
If we do not override the configure() method, a default filter chain is created as follows :
Each of these methods on the http object would lead to the addition of respective filters in the SecurityFilterChain. Inspect the methods, to know the individual filters being applied.
Here is a list of default filters that are applied:
You can enable DEBUG log level of the spring security package to track the filters in use.
So, with this, I have hopefully shown you what are the components involved in setting up the Security Filter chain and how they are interlinked. The initial purpose of the blog is served here.
But, if you are like me then you'd have thought of playing around with the existing setup and see what interesting things come up.
So, I did 2 things -
- Create another DelegateFilterProxy Bean with the default one and see what can be achieved in this stage.
- Create multiple Security Filter chains for different parts of our application.
Multiple DelegateFilterProxy Beans
Multiple DelegateFilterProxy Beans can be helpful when you want to hook your custom filter to the spring context. It can be done either before the Security chain kicks in or after it.
It is not necessary that you must absolutely invoke your custom filter via DelegateFilterProxy rather than hooking it directly, but it is always an option:)
In the following example, I have hooked the custom Filter before the Spring security chain. This is particularly useful in the Logging context. There are scenarios, where you'd like to log custom config in the MDC context for every web request. This can be useful while tracing logs in Kibana where you'd be presented with additional context injected via MDC.
Spring Security’s DelegateFilterProxy filter order is set at the value -100. Any value lower than this would indicate that it'll run before the security DelegateFilterProxy. And that's exactly what we would do here.
Creating a LogFilter -
And embedding it into custom DelegateFilterProxy.
This would configure the DelegateFilterProxy with the name LoggerDelegatingFilterProxy and would be invoked before the default DelegateFilterProxy that is invoked by Spring Security.
Multiple Security Filter Chains
In the second use case, I tried creating 2 different SecurityFilterChain that would be invoked on 2 different endpoints of the application.
Typically, such scenarios would arise when you have got parts of the application that is accessible by one mode of authentication, ex — Username Password and another part of the application that needs to access in a different way, ex — Oauth and the API for these two subsystems have different signatures.
However, to validate my use case, I just chose to add an extra filter — AuditLogFilter to only one of the SecurityFilterChain. This was to understand whether the setup is correct or not.
And finally, the 2 SecurityFilterChain Config -
The ManagerConfig is invoked only for manager API endpoints and this is where the AuditLog filter is configured.
The EmployeeConfig is the default security filter chain minus some filters.
When you invoke the respective APIs, you'd notice the different set of filters being applied.
With these two examples, I come to the end of this article. We have seen how the entire spring security architecture is plugged into the Spring framework by Spring boot and how we can customize it.
I thank you for reading till the end and hope that the information provided here was valuable to you.