13 KiB
servlet-architecture = Architecture :figures: servlet/architecture
This section discusses Spring Security's high level architecture within Servlet based applications. We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference. // FIXME: Add links to other sections of architecture
servlet-filters-review
== A Review of Filters
Spring Security's Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first.
The picture below shows the typical layering of the handlers for a single HTTP request.
.FilterChain servlet-filterchain-figure image::{figures}/filterchain.png[]
The client sends a request to the application, and the container creates a FilterChain which contains the Filters 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 {spring-framework-reference-url}web.html#mvc-servlet[DispatcherServlet].
At most one Servlet can handle a single HttpServletRequest and HttpServletResponse.
However, more than one Filter can be used to:
- Prevent downstream
Filters or theServletfrom being invoked. In this instance theFilterwill typically write theHttpServletResponse. - Modify the
HttpServletRequestorHttpServletResponseused by the downstreamFilters andServlet
The power of the Filter comes from the FilterChain that is passed into it.
.FilterChain Usage Example
.Java [source,java,role="primary"]
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 }
.Kotlin [source,kotlin,role="secondary"]
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 Filters and the Servlet, the order each Filter is invoked is extremely important.
servlet-delegatingfilterproxy == DelegatingFilterProxy
Spring provides a Filter implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] that allows bridging between the Servlet container's lifecycle and Spring's ApplicationContext.
The Servlet container allows registering Filters 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 <<servlet-filters-review,Filters and the FilterChain>>.
.DelegatingFilterProxy servlet-delegatingfilterproxy-figure image::{figures}/delegatingfilterproxy.png[]
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 [source,java,role="primary",subs="+quotes,+macros"]
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in <> delegate is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
.Kotlin [source,kotlin,role="secondary",subs="+quotes,+macros"]
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in <> 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.
servlet-filterchainproxy == 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 <<servlet-securityfilterchain,SecurityFilterChain>>.
Since FilterChainProxy is a Bean, it is typically wrapped in a <>.
.FilterChainProxy servlet-filterchainproxy-figure image::{figures}/filterchainproxy.png[]
servlet-securityfilterchain == SecurityFilterChain
{security-api-url}org/springframework/security/web/SecurityFilterChain.html[SecurityFilterChain] is used by <> to determine which Spring Security Filters should be invoked for this request.
.SecurityFilterChain servlet-securityfilterchain-figure image::{figures}/securityfilterchain.png[]
The <<servlet-security-filters,Security Filters>> in SecurityFilterChain are typically Beans, but they are registered with FilterChainProxy instead of <>.
FilterChainProxy provides a number of advantages to registering directly with the Servlet container or <>.
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.
// FIXME: Add a link to SecurityContext
For example, it clears out the SecurityContext to avoid memory leaks.
It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[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, Filters are invoked based upon the URL alone.
// FIXME: Link to RequestMatcher
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.
.Multiple SecurityFilterChain servlet-multi-securityfilterchain-figure image::{figures}/multi-securityfilterchain.png[]
In the <> 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 SecurityFilterChain~0~'s pattern of +/api/**+, so only SecurityFilterChain~0~ will be invoked even though it also matches on SecurityFilterChain~n~.
If a URL of /messages/ is requested, it will not match on SecurityFilterChain~0~'s pattern of +/api/**+, so FilterChainProxy will continue trying each SecurityFilterChain.
Assuming that no other, SecurityFilterChain instances match SecurityFilterChain~n~ will be invoked.
// FIXME add link to pattern matching
Notice that SecurityFilterChain~0~ has only three security Filters instances configured.
However, SecurityFilterChain~n~ has four security Filters configured.
It is important to note that each SecurityFilterChain can be unique and configured in isolation.
In fact, a SecurityFilterChain might have zero security Filters if the application wants Spring Security to ignore certain requests.
// FIXME: add link to configuring multiple SecurityFilterChain instances
servlet-security-filters == Security Filters
The Security Filters are inserted into the <> with the <> API.
The <<servlet-filters-review,order of Filter>>s matters.
It is typically not necessary to know the ordering of Spring Security's Filters.
However, there are times that it is beneficial to know the ordering
Below is a comprehensive list of Spring Security Filter ordering:
- xref:servlet/authentication/session-management.adoc#session-mgmt-force-session-creation[
ForceEagerSessionCreationFilter] - ChannelProcessingFilter
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[
UsernamePasswordAuthenticationFilter] - OpenIDAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- ConcurrentSessionFilter
- xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[
DigestAuthenticationFilter] - BearerTokenAuthenticationFilter
- xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[
BasicAuthenticationFilter] - RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- OAuth2AuthorizationCodeGrantFilter
- SessionManagementFilter
- <<servlet-exceptiontranslationfilter,
ExceptionTranslationFilter>> - xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[
FilterSecurityInterceptor] - SwitchUserFilter
servlet-exceptiontranslationfilter == Handling Security Exceptions
The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[ExceptionTranslationFilter] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[AccessDeniedException] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[AuthenticationException] into HTTP responses.
ExceptionTranslationFilter is inserted into the <> as one of the <>.
image::{figures}/exceptiontranslationfilter.png[]
- image:{icondir}/number_1.png[] First, the
ExceptionTranslationFilterinvokesFilterChain.doFilter(request, response)to invoke the rest of the application. - image:{icondir}/number_2.png[] If the user is not authenticated or it is an
AuthenticationException, then Start Authentication. ** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out ** TheHttpServletRequestis saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[RequestCache]. When the user successfully authenticates, theRequestCacheis used to replay the original request. // FIXME: add link to authentication success ** TheAuthenticationEntryPointis used to request credentials from the client. For example, it might redirect to a log in page or send aWWW-Authenticateheader. // FIXME: link to AuthenticationEntryPoint - image:{icondir}/number_3.png[] Otherwise if it is an
AccessDeniedException, then Access Denied. TheAccessDeniedHandleris invoked to handle access denied. // FIXME: link to AccessDeniedHandler
[NOTE]
If the application does not throw an AccessDeniedException or an AuthenticationException, then ExceptionTranslationFilter does not do anything.
The pseudocode for ExceptionTranslationFilter looks something like this:
.ExceptionTranslationFilter pseudocode [source,java]
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 <> 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. xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[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