This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.4.2! |
Architecture
This section discusses Spring Security’s high level architecture within Servlet based applications. We build on this high level understanding within Authentication, Authorization, Protection Against Exploits sections of the reference.
A Review of Filter
s
Spring Security’s Servlet support is based on Servlet Filter
s, so it is helpful to look at the role of Filter
s generally first.
The picture below shows the typical layering of the handlers for a single HTTP request.
The client sends a request to the application, and the container creates a FilterChain
which contains the Filter
s and Servlet
that should process the HttpServletRequest
based on the path of the request URI.
In a Spring MVC application the Servlet
is an instance of DispatcherServlet
.
At most one Servlet
can handle a single HttpServletRequest
and HttpServletResponse
.
However, more than one Filter
can be used to:
-
Prevent downstream
Filter
s or theServlet
from being invoked. In this instance theFilter
will typically write theHttpServletResponse
. -
Modify the
HttpServletRequest
orHttpServletResponse
used by the downstreamFilter
s andServlet
The power of the Filter
comes from the FilterChain
that is passed into it.
FilterChain
Usage Example-
Java
-
Kotlin
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// do something before the rest of the application
chain.doFilter(request, response); // invoke the rest of the application
// do something after the rest of the application
}
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// do something before the rest of the application
chain.doFilter(request, response) // invoke the rest of the application
// do something after the rest of the application
}
Since a Filter
only impacts downstream Filter
s and the Servlet
, the order each Filter
is invoked is extremely important.
DelegatingFilterProxy
Spring provides a Filter
implementation named DelegatingFilterProxy
that allows bridging between the Servlet container’s lifecycle and Spring’s ApplicationContext
.
The Servlet container allows registering Filter
s using its own standards, but it is not aware of Spring defined Beans.
DelegatingFilterProxy
can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements Filter
.
Here is a picture of how DelegatingFilterProxy
fits into the Filter
s and the FilterChain
.
DelegatingFilterProxy
looks up Bean Filter0 from the ApplicationContext
and then invokes Bean Filter0.
The pseudo code of DelegatingFilterProxy
can be seen below.
DelegatingFilterProxy
Pseudo Code-
Java
-
Kotlin
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate
is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate
is an instance of Bean Filter0
val delegate: Filter = getFilterBean(someBeanName)
// delegate work to the Spring Bean
delegate.doFilter(request, response)
}
Another benefit of DelegatingFilterProxy
is that it allows delaying looking Filter
bean instances.
This is important because the container needs to register the Filter
instances before the container can startup.
However, Spring typically uses a ContextLoaderListener
to load the Spring Beans which will not be done until after the Filter
instances need to be registered.
FilterChainProxy
Spring Security’s Servlet support is contained within FilterChainProxy
.
FilterChainProxy
is a special Filter
provided by Spring Security that allows delegating to many Filter
instances through SecurityFilterChain
.
Since FilterChainProxy
is a Bean, it is typically wrapped in a DelegatingFilterProxy.
SecurityFilterChain
SecurityFilterChain
is used by FilterChainProxy to determine which Spring Security Filter
s should be invoked for this request.
The Security Filters in SecurityFilterChain
are typically Beans, but they are registered with FilterChainProxy
instead of DelegatingFilterProxy.
FilterChainProxy
provides a number of advantages to registering directly with the Servlet container or DelegatingFilterProxy.
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.
Second, since FilterChainProxy
is central to Spring Security usage it can perform tasks that are not viewed as optional.
For example, it clears out the SecurityContext
to avoid memory leaks.
It also applies Spring Security’s HttpFirewall
to protect applications against certain types of attacks.
In addition, it provides more flexibility in determining when a SecurityFilterChain
should be invoked.
In a Servlet container, Filter
s are invoked based upon the URL alone.
However, FilterChainProxy
can determine invocation based upon anything in the HttpServletRequest
by leveraging the RequestMatcher
interface.
In fact, FilterChainProxy
can be used to determine which SecurityFilterChain
should be used.
This allows providing a totally separate configuration for different slices of your application.
In the Multiple SecurityFilterChain Figure FilterChainProxy
decides which SecurityFilterChain
should be used.
Only the first SecurityFilterChain
that matches will be invoked.
If a URL of /api/messages/
is requested, it will first match on SecurityFilterChain0
's pattern of /api/**
, so only SecurityFilterChain0
will be invoked even though it also matches on SecurityFilterChainn
.
If a URL of /messages/
is requested, it will not match on SecurityFilterChain0
's pattern of /api/**
, so FilterChainProxy
will continue trying each SecurityFilterChain
.
Assuming that no other, SecurityFilterChain
instances match SecurityFilterChainn
will be invoked.
Notice that SecurityFilterChain0
has only three security Filter
s instances configured.
However, SecurityFilterChainn
has four security Filter
s configured.
It is important to note that each SecurityFilterChain
can be unique and configured in isolation.
In fact, a SecurityFilterChain
might have zero security Filter
s if the application wants Spring Security to ignore certain requests.
Security Filters
The Security Filters are inserted into the FilterChainProxy with the SecurityFilterChain API.
The order of Filter
s matters.
It is typically not necessary to know the ordering of Spring Security’s Filter
s.
However, there are times that it is beneficial to know the ordering
Below is a comprehensive list of Spring Security Filter ordering:
-
ChannelProcessingFilter
-
WebAsyncManagerIntegrationFilter
-
SecurityContextPersistenceFilter
-
HeaderWriterFilter
-
CorsFilter
-
CsrfFilter
-
LogoutFilter
-
OAuth2AuthorizationRequestRedirectFilter
-
Saml2WebSsoAuthenticationRequestFilter
-
X509AuthenticationFilter
-
AbstractPreAuthenticatedProcessingFilter
-
CasAuthenticationFilter
-
OAuth2LoginAuthenticationFilter
-
Saml2WebSsoAuthenticationFilter
-
OpenIDAuthenticationFilter
-
DefaultLoginPageGeneratingFilter
-
DefaultLogoutPageGeneratingFilter
-
ConcurrentSessionFilter
-
BearerTokenAuthenticationFilter
-
RequestCacheAwareFilter
-
SecurityContextHolderAwareRequestFilter
-
JaasApiIntegrationFilter
-
RememberMeAuthenticationFilter
-
AnonymousAuthenticationFilter
-
OAuth2AuthorizationCodeGrantFilter
-
SessionManagementFilter
-
SwitchUserFilter
Handling Security Exceptions
The ExceptionTranslationFilter
allows translation of AccessDeniedException
and AuthenticationException
into HTTP responses.
ExceptionTranslationFilter
is inserted into the FilterChainProxy as one of the Security Filters.
-
First, the
ExceptionTranslationFilter
invokesFilterChain.doFilter(request, response)
to invoke the rest of the application. -
If the user is not authenticated or it is an
AuthenticationException
, then Start Authentication.-
The SecurityContextHolder is cleared out
-
The
HttpServletRequest
is saved in theRequestCache
. When the user successfully authenticates, theRequestCache
is used to replay the original request. -
The
AuthenticationEntryPoint
is used to request credentials from the client. For example, it might redirect to a log in page or send aWWW-Authenticate
header.
-
-
Otherwise if it is an
AccessDeniedException
, then Access Denied. TheAccessDeniedHandler
is invoked to handle access denied.
If the application does not throw an |
The pseudocode for ExceptionTranslationFilter
looks something like this:
try {
filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); (2)
} else {
accessDenied(); (3)
}
}
1 | You will recall from A Review of Filter s that invoking FilterChain.doFilter(request, response) is the equivalent of invoking the rest of the application.
This means that if another part of the application, (i.e. FilterSecurityInterceptor or method security) throws an AuthenticationException or AccessDeniedException it will be caught and handled here. |
2 | If the user is not authenticated or it is an AuthenticationException , then Start Authentication. |
3 | Otherwise, Access Denied |