mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 06:38:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			250 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| [[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 ``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.
 | |
| 
 | |
| .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 ``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 {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 ``Filter``s or the `Servlet` from being invoked.
 | |
| In this instance the `Filter` will typically write the `HttpServletResponse`.
 | |
| * Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet`
 | |
| 
 | |
| 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 ``Filter``s 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 ``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 <<servlet-filters-review,``Filter``s and the `FilterChain`>>.
 | |
| 
 | |
| .DelegatingFilterProxy
 | |
| [[servlet-delegatingfilterproxy-figure]]
 | |
| image::{figures}/delegatingfilterproxy.png[]
 | |
| 
 | |
| `DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__.
 | |
| 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 <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__
 | |
| 	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 <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__
 | |
| 	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 <<servlet-delegatingfilterproxy>>.
 | |
| 
 | |
| .FilterChainProxy
 | |
| [[servlet-filterchainproxy-figure]]
 | |
| image::{figures}/filterchainproxy.png[]
 | |
| 
 | |
| [[servlet-securityfilterchain]]
 | |
| == SecurityFilterChain
 | |
| 
 | |
| {security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <<servlet-filterchainproxy>> to determine which Spring Security ``Filter``s 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 <<servlet-delegatingfilterproxy>>.
 | |
| `FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <<servlet-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.
 | |
| // 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, ``Filter``s 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 <<servlet-multi-securityfilterchain-figure>> 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 ``Filter``s instances configured.
 | |
| However, `SecurityFilterChain~n~` 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.
 | |
| // FIXME: add link to configuring multiple `SecurityFilterChain` instances
 | |
| 
 | |
| [[servlet-security-filters]]
 | |
| == Security Filters
 | |
| 
 | |
| The Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API.
 | |
| The <<servlet-filters-review,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:
 | |
| 
 | |
| * 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 <<servlet-filterchainproxy>> as one of the <<servlet-security-filters>>.
 | |
| 
 | |
| image::{figures}/exceptiontranslationfilter.png[]
 | |
| 
 | |
| 
 | |
| * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.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
 | |
| ** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`].
 | |
| When the user successfully authenticates, the `RequestCache` is used to replay the original request.
 | |
| // FIXME: add link to authentication success
 | |
| ** The `AuthenticationEntryPoint` is used to request credentials from the client.
 | |
| For example, it might redirect to a log in page or send a `WWW-Authenticate` header.
 | |
| // FIXME: link to AuthenticationEntryPoint
 | |
| * image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__.
 | |
| The `AccessDeniedHandler` is 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 <<servlet-filters-review>> 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__
 |