In this blog post, we’ll take a deep dive into Spring Security’s authentication mechanism and how it integrates with Spring Web MVC. Using a JWT as an example, we will explore the main classes in the Spring Security authentication mechanism and how they work together.
Spring Security and Spring Web MVC - General Architecture
Spring Security is an authentication and authorization framework for securing Spring-based applications. Because of its integration with Spring Web MVC, this is also true for web applications.
In this section, I want to describe how these two Spring projects are integrated with each other.
The first component we are interested in right now is the DelegatingFilterProxy
of Spring Web MVC. It is a servlet filter as described in the Jakarta servlet specification. In general, these filters are responsible for performing filtering tasks on either the request, the response, or both. In a Spring web application, they are called by the servlet container (Catalina) inside the web server (Tomcat). In particular, the DelegatingFilterProxy is responsible for delegating requests to the FilterChainProxy - a Spring bean, a servlet filter, and part of Spring Security. This means that DelegatingFilterProxy acts as a bridge between the servlet and the Spring world.
The FilterChainProxy itself has a list of SecurityFilterChain
s, to which it delegates its requests. The SecurityFilterChain is a Spring bean that is typically provided by our application. For example:
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authorize -> {
authorize.requestMatchers("/admin/**").authenticated();
authorize.anyRequest().permitAll();
})
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.build();
}
}
The SecurityFilterChain has a list of servlet filters, that handle security-related concerns. In this example, it will contain a BearerTokenAuthenticationFilter
because of the ::oauth2ResourceServer()
method.
Authentication in the Context of Spring Web MVC
With the general architecture explained, we can now take a deeper look at the authentication mechanism of Spring Security. Again, we will start with an overview of the classes involved.
This diagram shows the classes involved in authentication using a JWT as the authentication token. Before we have a look at how they work together, let us first discuss the responsibilities of these classes.
FilterChainProxy
The entry point to Spring Security from Spring Web MVC. The instance of this class is managed by Spring’s Inversion of Control (IoC) container. It contains a list of SecurityFilterChains to which it delegates its requests.SecurityFilterChain
Contains a RequestMatcher and a list of Jakarta Servlet filters. The RequestMatcher is used by the FilterChainProxy to determine whether a SecurityFilterChain is responsible for a request or not. If it is, its filters are invoked.BearerTokenAuthenticationFilter
Extracts an Authentication object (in this particular case, a BearerTokenAuthenticationToken) from the information given in a request, passes it to its AuthenticationManager, and handles the result by setting a SecurityContext.BearerTokenAuthenticationToken
Contains the bearer token from the request that was extracted by the BearerTokenAuthenticationFilter.Authentication
Holds the information used to authenticate a user. If the authentication was successful, an Authentication object is stored in the SecurityContext.AuthenticationManager
Handles the actual authentication request.ProviderManager
Is the default implementation of the AuthenticationManager. It holds a list of AuthenticationProviders.AuthenticationProvider
Handles the actual authentication.JwtAuthenticationProvider
A possible implementation of an AuthenticationProvider that supports BearerTokenAuthenticationTokens for authentication.
Now that we know the main classes and their responsibilities, let us take a closer look at their interactions with each other. Note that I left out details in this diagram to make it easier to understand. It mainly shows one path through the authentication mechanism, ignoring all loops, most logical branches, and many classes.
As discussed earlier, the FilterChainProxy
is the starting point of the authentication mechanism. It checks if a SecurityFilterChain
is responsible for the current request and asks for all of its filters. After receiving the filters, it invokes each of them. The BearerTokenAuthenticationFilter
in our example then extracts the JWT Bearer token from the request and passes it as a BearerTokenAuthenticationToken to the ProviderManager
. The ProviderManager checks to see if any of its AuthenticationProviders can perform authentication with this information, and invokes them. In our case, the JwtAuthenticationProvider
can handle the given BearerTokenAuthenticationToken and tries to decode the JWT. This object is the component, that does the actual authentication. If it is successful, it creates an AbstractAuthenticationToken
and returns it all the way back to the BearerTokenAuthenticationFilter. This filter uses the Authentication object to create a new SecurityContext. If the authentication fails, an exception is thrown by the JwtAuthenticationProvider and caught and handled by the BearerTokenAuthenticationFilter.
Conclusion
As I worked my way through the code to understand Spring’s authentication mechanism, I first thought it was horribly complicated. But by abstracting away some of the details, I was finally able to understand the intent of the code. The official documentation describes Spring Security as follows:
Spring Security is a powerful and highly customizable authentication and access-control framework.
One of the main goals of Spring Security is to be highly customizable to support a wide range of different authorization processes. The JWT handling classes we used in this example are part of spring-security-oauth2-resource-server - an additional module within Spring Security. By extending a few classes, the authors were able to plug this functionality directly into the framework. All thanks to the seemingly complicated architecture of the project.
Related Resources
- The official documentation of Spring Security