architecture.adoc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. [[servlet-architecture]]
  2. = Architecture
  3. :figures: servlet/architecture
  4. This section discusses Spring Security's high-level architecture within Servlet based applications.
  5. We build on this high-level understanding within the xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], and xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference.
  6. // FIXME: Add links to other sections of architecture
  7. [[servlet-filters-review]]
  8. == A Review of Filters
  9. Spring Security's Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first.
  10. The following image shows the typical layering of the handlers for a single HTTP request.
  11. .FilterChain
  12. [[servlet-filterchain-figure]]
  13. image::{figures}/filterchain.png[]
  14. The client sends a request to the application, and the container creates a `FilterChain`, which contains the `Filter` instances and `Servlet` that should process the `HttpServletRequest`, based on the path of the request URI.
  15. In a Spring MVC application, the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`].
  16. At most, one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`.
  17. However, more than one `Filter` can be used to:
  18. * Prevent downstream `Filter` instances or the `Servlet` from being invoked.
  19. In this case, the `Filter` typically writes the `HttpServletResponse`.
  20. * Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream `Filter` instances and the `Servlet`.
  21. The power of the `Filter` comes from the `FilterChain` that is passed into it.
  22. .`FilterChain` Usage Example
  23. ====
  24. .Java
  25. [source,java,role="primary"]
  26. ----
  27. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
  28. // do something before the rest of the application
  29. chain.doFilter(request, response); // invoke the rest of the application
  30. // do something after the rest of the application
  31. }
  32. ----
  33. .Kotlin
  34. [source,kotlin,role="secondary"]
  35. ----
  36. fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
  37. // do something before the rest of the application
  38. chain.doFilter(request, response) // invoke the rest of the application
  39. // do something after the rest of the application
  40. }
  41. ----
  42. ====
  43. Since a `Filter` impacts only downstream `Filter` instances and the `Servlet`, the order in which each `Filter` is invoked is extremely important.
  44. [[servlet-delegatingfilterproxy]]
  45. == DelegatingFilterProxy
  46. 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`.
  47. The Servlet container allows registering `Filter` instances by using its own standards, but it is not aware of Spring-defined Beans.
  48. You can register `DelegatingFilterProxy` through the standard Servlet container mechanisms but delegate all the work to a Spring Bean that implements `Filter`.
  49. Here is a picture of how `DelegatingFilterProxy` fits into the <<servlet-filters-review,`Filter` instances and the `FilterChain`>>.
  50. .DelegatingFilterProxy
  51. [[servlet-delegatingfilterproxy-figure]]
  52. image::{figures}/delegatingfilterproxy.png[]
  53. `DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__.
  54. The following listing shows pseudo code of `DelegatingFilterProxy`:
  55. .`DelegatingFilterProxy` Pseudo Code
  56. ====
  57. .Java
  58. [source,java,role="primary",subs="+quotes,+macros"]
  59. ----
  60. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
  61. // Lazily get Filter that was registered as a Spring Bean
  62. // For the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__
  63. Filter delegate = getFilterBean(someBeanName);
  64. // delegate work to the Spring Bean
  65. delegate.doFilter(request, response);
  66. }
  67. ----
  68. .Kotlin
  69. [source,kotlin,role="secondary",subs="+quotes,+macros"]
  70. ----
  71. fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
  72. // Lazily get Filter that was registered as a Spring Bean
  73. // For the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__
  74. val delegate: Filter = getFilterBean(someBeanName)
  75. // delegate work to the Spring Bean
  76. delegate.doFilter(request, response)
  77. }
  78. ----
  79. ====
  80. Another benefit of `DelegatingFilterProxy` is that it allows delaying looking up `Filter` bean instances.
  81. This is important because the container needs to register the `Filter` instances before the container can start up.
  82. However, Spring typically uses a `ContextLoaderListener` to load the Spring Beans, which is not done until after the `Filter` instances need to be registered.
  83. [[servlet-filterchainproxy]]
  84. == FilterChainProxy
  85. Spring Security's Servlet support is contained within `FilterChainProxy`.
  86. `FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <<servlet-securityfilterchain,`SecurityFilterChain`>>.
  87. Since `FilterChainProxy` is a Bean, it is typically wrapped in a <<servlet-delegatingfilterproxy>>.
  88. The following image shows the role of `FilterChainProxy`.
  89. .FilterChainProxy
  90. [[servlet-filterchainproxy-figure]]
  91. image::{figures}/filterchainproxy.png[]
  92. [[servlet-securityfilterchain]]
  93. == SecurityFilterChain
  94. {security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <<servlet-filterchainproxy>> to determine which Spring Security `Filter` instances should be invoked for the current request.
  95. The following image shows the role of `SecurityFilterChain`.
  96. .SecurityFilterChain
  97. [[servlet-securityfilterchain-figure]]
  98. image::{figures}/securityfilterchain.png[]
  99. The <<servlet-security-filters,Security Filters>> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <<servlet-delegatingfilterproxy>>.
  100. `FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <<servlet-delegatingfilterproxy>>.
  101. First, it provides a starting point for all of Spring Security's Servlet support.
  102. For that reason, if you try to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start.
  103. Second, since `FilterChainProxy` is central to Spring Security usage, it can perform tasks that are not viewed as optional.
  104. // FIXME: Add a link to SecurityContext
  105. For example, it clears out the `SecurityContext` to avoid memory leaks.
  106. It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks.
  107. In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked.
  108. In a Servlet container, `Filter` instances are invoked based upon the URL alone.
  109. // FIXME: Link to RequestMatcher
  110. However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by using the `RequestMatcher` interface.
  111. The following image shows multiple `SecurityFilterChain` instances:
  112. .Multiple SecurityFilterChain
  113. [[servlet-multi-securityfilterchain-figure]]
  114. image::{figures}/multi-securityfilterchain.png[]
  115. In the <<servlet-multi-securityfilterchain-figure>> figure, `FilterChainProxy` decides which `SecurityFilterChain` should be used.
  116. Only the first `SecurityFilterChain` that matches is invoked.
  117. If a URL of `/api/messages/` is requested, it first matches on the `SecurityFilterChain~0~` pattern of `+/api/**+`, so only `SecurityFilterChain~0~` is invoked, even though it also matches on ``SecurityFilterChain~n~``.
  118. If a URL of `/messages/` is requested, it does not match on the `SecurityFilterChain~0~` pattern of `+/api/**+`, so `FilterChainProxy` continues trying each `SecurityFilterChain`.
  119. Assuming that no other `SecurityFilterChain` instances match, `SecurityFilterChain~n~` is invoked.
  120. // FIXME: add link to pattern matching
  121. Notice that `SecurityFilterChain~0~` has only three security `Filter` instances configured.
  122. However, `SecurityFilterChain~n~` has four security `Filter` instanes configured.
  123. It is important to note that each `SecurityFilterChain` can be unique and can be configured in isolation.
  124. In fact, a `SecurityFilterChain` might have zero security `Filter` instances if the application wants Spring Security to ignore certain requests.
  125. // FIXME: add link to configuring multiple `SecurityFilterChain` instances
  126. [[servlet-security-filters]]
  127. == Security Filters
  128. The Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API.
  129. The <<servlet-filters-review,order of `Filter`>> instances matters.
  130. It is typically not necessary to know the ordering of Spring Security's `Filter` instances.
  131. However, there are times that it is beneficial to know the ordering.
  132. The following is a comprehensive list of Spring Security Filter ordering:
  133. * `ChannelProcessingFilter`
  134. * `WebAsyncManagerIntegrationFilter`
  135. * `SecurityContextPersistenceFilter`
  136. * `HeaderWriterFilter`
  137. * `CorsFilter`
  138. * `CsrfFilter`
  139. * `LogoutFilter`
  140. * `OAuth2AuthorizationRequestRedirectFilter`
  141. * `Saml2WebSsoAuthenticationRequestFilter`
  142. * `X509AuthenticationFilter`
  143. * `AbstractPreAuthenticatedProcessingFilter`
  144. * `CasAuthenticationFilter`
  145. * `OAuth2LoginAuthenticationFilter`
  146. * `Saml2WebSsoAuthenticationFilter`
  147. * xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`]
  148. * `OpenIDAuthenticationFilter`
  149. * `DefaultLoginPageGeneratingFilter`
  150. * `DefaultLogoutPageGeneratingFilter`
  151. * `ConcurrentSessionFilter`
  152. * xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`]
  153. * `BearerTokenAuthenticationFilter`
  154. * xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`]
  155. * `RequestCacheAwareFilter`
  156. * `SecurityContextHolderAwareRequestFilter`
  157. * `JaasApiIntegrationFilter`
  158. * `RememberMeAuthenticationFilter`
  159. * `AnonymousAuthenticationFilter`
  160. * `OAuth2AuthorizationCodeGrantFilter`
  161. * `SessionManagementFilter`
  162. * <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>>
  163. * xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`]
  164. * `SwitchUserFilter`
  165. [[servlet-exceptiontranslationfilter]]
  166. == Handling Security Exceptions
  167. 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.
  168. `ExceptionTranslationFilter` is inserted into the <<servlet-filterchainproxy>> as one of the <<servlet-security-filters>>.
  169. The following image shows the relationship of `ExceptionTranslationFilter` to other components:
  170. image::{figures}/exceptiontranslationfilter.png[]
  171. * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application.
  172. * image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__.
  173. ** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.
  174. ** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`].
  175. When the user successfully authenticates, the `RequestCache` is used to replay the original request.
  176. // FIXME: add link to authentication success
  177. ** The `AuthenticationEntryPoint` is used to request credentials from the client.
  178. For example, it might redirect to a log in page or send a `WWW-Authenticate` header.
  179. // FIXME: link to AuthenticationEntryPoint
  180. * image:{icondir}/number_3.png[] Otherwise, if it is an `AccessDeniedException`, then __Access Denied__.
  181. The `AccessDeniedHandler` is invoked to handle access denied.
  182. // FIXME: link to AccessDeniedHandler
  183. [NOTE]
  184. ====
  185. If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything.
  186. ====
  187. The pseudocode for `ExceptionTranslationFilter` looks something like this:
  188. ====
  189. .ExceptionTranslationFilter pseudocode
  190. [source,java]
  191. ----
  192. try {
  193. filterChain.doFilter(request, response); // <1>
  194. } catch (AccessDeniedException | AuthenticationException ex) {
  195. if (!authenticated || ex instanceof AuthenticationException) {
  196. startAuthentication(); // <2>
  197. } else {
  198. accessDenied(); // <3>
  199. }
  200. }
  201. ----
  202. <1> As described in <<servlet-filters-review>>, invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application.
  203. This means that if another part of the application, (<<servlet-authorization-filtersecurityinterceptor,`FilterSecurityInterceptor`>> or method security) throws an `AuthenticationException` or `AccessDeniedException` it is caught and handled here.
  204. <2> If the user is not authenticated or it is an `AuthenticationException`, __Start Authentication__.
  205. <3> Otherwise, __Access Denied__
  206. ====