From e368734557e4c8bf67db3be02b28977fd3011470 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 20 Sep 2021 16:57:37 -0500 Subject: [PATCH] Update "Big Picture" to Architecture --- docs/modules/ROOT/nav.adoc | 2 +- .../ROOT/pages/servlet/architecture.adoc | 2 +- .../pages/servlet/architecture/index.adoc | 248 ------------------ 3 files changed, 2 insertions(+), 250 deletions(-) delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/index.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index c5a915faa9..b6819592eb 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -14,7 +14,7 @@ * xref:samples.adoc[Samples] * xref:servlet/index.adoc[Servlet Applications] ** xref:servlet/getting-started.adoc[Getting Started] -** xref:servlet/architecture.adoc[The Big Picture] +** xref:servlet/architecture.adoc[Architecture] ** xref:servlet/authentication/index.adoc[Authentication] *** xref:servlet/authentication/architecture.adoc[Authentication Architecture] *** xref:servlet/authentication/passwords/index.adoc[Username/Password] diff --git a/docs/modules/ROOT/pages/servlet/architecture.adoc b/docs/modules/ROOT/pages/servlet/architecture.adoc index ea457e03bb..40b62a1a3c 100644 --- a/docs/modules/ROOT/pages/servlet/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture.adoc @@ -1,5 +1,5 @@ [[servlet-architecture]] -= Servlet Security: The Big Picture += Architecture :figures: servlet/architecture This section discusses Spring Security's high level architecture within Servlet based applications. diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc deleted file mode 100644 index a1120e1585..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ /dev/null @@ -1,248 +0,0 @@ -[[servlet-architecture]] -= Servlet Security: The Big Picture -: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 <>. - -.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 <> `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 <> `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 up. -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 <>. -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 ``Filter``s should be invoked for this request. - -.SecurityFilterChain -[[servlet-securityfilterchain-figure]] -image::{figures}/securityfilterchain.png[] - -The <> 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, ``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 <> 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 <> with the <> API. -The <>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 -* 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 -* <> -* 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 `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 <> 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__