2
0

architecture.adoc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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 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.
  6. // FIXME: Add links to other sections of architecture
  7. [[servlet-filters-review]]
  8. == A Review of ``Filter``s
  9. 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.
  10. The picture below 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``s 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``s or the `Servlet` from being invoked.
  19. In this instance the `Filter` will typically write the `HttpServletResponse`.
  20. * Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet`
  21. The power of the `Filter` comes from the `FilterChain` that is passed into it.
  22. .`FilterChain` Usage Example
  23. [tabs]
  24. ======
  25. Java::
  26. +
  27. [source,java,role="primary"]
  28. ----
  29. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
  30. // do something before the rest of the application
  31. chain.doFilter(request, response); // invoke the rest of the application
  32. // do something after the rest of the application
  33. }
  34. ----
  35. Kotlin::
  36. +
  37. [source,kotlin,role="secondary"]
  38. ----
  39. fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
  40. // do something before the rest of the application
  41. chain.doFilter(request, response) // invoke the rest of the application
  42. // do something after the rest of the application
  43. }
  44. ----
  45. ======
  46. Since a `Filter` only impacts downstream ``Filter``s and the `Servlet`, the order each `Filter` is invoked is extremely important.
  47. [[servlet-delegatingfilterproxy]]
  48. == DelegatingFilterProxy
  49. 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`.
  50. The Servlet container allows registering ``Filter``s using its own standards, but it is not aware of Spring defined Beans.
  51. `DelegatingFilterProxy` can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements `Filter`.
  52. Here is a picture of how `DelegatingFilterProxy` fits into the <<servlet-filters-review,``Filter``s and the `FilterChain`>>.
  53. .DelegatingFilterProxy
  54. [[servlet-delegatingfilterproxy-figure]]
  55. image::{figures}/delegatingfilterproxy.png[]
  56. `DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__.
  57. The pseudo code of `DelegatingFilterProxy` can be seen below.
  58. .`DelegatingFilterProxy` Pseudo Code
  59. [tabs]
  60. ======
  61. Java::
  62. +
  63. [source,java,role="primary",subs="+quotes,+macros"]
  64. ----
  65. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
  66. // Lazily get Filter that was registered as a Spring Bean
  67. // For the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__
  68. Filter delegate = getFilterBean(someBeanName);
  69. // delegate work to the Spring Bean
  70. delegate.doFilter(request, response);
  71. }
  72. ----
  73. Kotlin::
  74. +
  75. [source,kotlin,role="secondary",subs="+quotes,+macros"]
  76. ----
  77. fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
  78. // Lazily get Filter that was registered as a Spring Bean
  79. // For the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__
  80. val delegate: Filter = getFilterBean(someBeanName)
  81. // delegate work to the Spring Bean
  82. delegate.doFilter(request, response)
  83. }
  84. ----
  85. ======
  86. Another benefit of `DelegatingFilterProxy` is that it allows delaying looking `Filter` bean instances.
  87. This is important because the container needs to register the `Filter` instances before the container can startup.
  88. 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.
  89. [[servlet-filterchainproxy]]
  90. == FilterChainProxy
  91. Spring Security's Servlet support is contained within `FilterChainProxy`.
  92. `FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <<servlet-securityfilterchain,`SecurityFilterChain`>>.
  93. Since `FilterChainProxy` is a Bean, it is typically wrapped in a <<servlet-delegatingfilterproxy>>.
  94. .FilterChainProxy
  95. [[servlet-filterchainproxy-figure]]
  96. image::{figures}/filterchainproxy.png[]
  97. [[servlet-securityfilterchain]]
  98. == SecurityFilterChain
  99. {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.
  100. .SecurityFilterChain
  101. [[servlet-securityfilterchain-figure]]
  102. image::{figures}/securityfilterchain.png[]
  103. The <<servlet-security-filters,Security Filters>> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <<servlet-delegatingfilterproxy>>.
  104. `FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <<servlet-delegatingfilterproxy>>.
  105. First, it provides a starting point for all of Spring Security's Servlet support.
  106. 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.
  107. Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional.
  108. // FIXME: Add a link to SecurityContext
  109. For example, it clears out the `SecurityContext` to avoid memory leaks.
  110. It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks.
  111. In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked.
  112. In a Servlet container, ``Filter``s are invoked based upon the URL alone.
  113. // FIXME: Link to RequestMatcher
  114. However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by leveraging the `RequestMatcher` interface.
  115. In fact, `FilterChainProxy` can be used to determine which `SecurityFilterChain` should be used.
  116. This allows providing a totally separate configuration for different _slices_ of your application.
  117. .Multiple SecurityFilterChain
  118. [[servlet-multi-securityfilterchain-figure]]
  119. image::{figures}/multi-securityfilterchain.png[]
  120. In the <<servlet-multi-securityfilterchain-figure>> Figure `FilterChainProxy` decides which `SecurityFilterChain` should be used.
  121. Only the first `SecurityFilterChain` that matches will be invoked.
  122. 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~``.
  123. 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`.
  124. Assuming that no other, `SecurityFilterChain` instances match `SecurityFilterChain~n~` will be invoked.
  125. // FIXME add link to pattern matching
  126. Notice that `SecurityFilterChain~0~` has only three security ``Filter``s instances configured.
  127. However, `SecurityFilterChain~n~` has four security ``Filter``s configured.
  128. It is important to note that each `SecurityFilterChain` can be unique and configured in isolation.
  129. In fact, a `SecurityFilterChain` might have zero security ``Filter``s if the application wants Spring Security to ignore certain requests.
  130. // FIXME: add link to configuring multiple `SecurityFilterChain` instances
  131. [[servlet-security-filters]]
  132. == Security Filters
  133. The Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API.
  134. The <<servlet-filters-review,order of ``Filter``>>s matters.
  135. It is typically not necessary to know the ordering of Spring Security's ``Filter``s.
  136. However, there are times that it is beneficial to know the ordering
  137. Below is a comprehensive list of Spring Security Filter ordering:
  138. * xref:servlet/authentication/session-management.adoc#session-mgmt-force-session-creation[`ForceEagerSessionCreationFilter`]
  139. * ChannelProcessingFilter
  140. * WebAsyncManagerIntegrationFilter
  141. * SecurityContextPersistenceFilter
  142. * HeaderWriterFilter
  143. * CorsFilter
  144. * CsrfFilter
  145. * LogoutFilter
  146. * OAuth2AuthorizationRequestRedirectFilter
  147. * Saml2WebSsoAuthenticationRequestFilter
  148. * X509AuthenticationFilter
  149. * AbstractPreAuthenticatedProcessingFilter
  150. * CasAuthenticationFilter
  151. * OAuth2LoginAuthenticationFilter
  152. * Saml2WebSsoAuthenticationFilter
  153. * xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`]
  154. * OpenIDAuthenticationFilter
  155. * DefaultLoginPageGeneratingFilter
  156. * DefaultLogoutPageGeneratingFilter
  157. * ConcurrentSessionFilter
  158. * xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`]
  159. * BearerTokenAuthenticationFilter
  160. * xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`]
  161. * <<requestcacheawarefilter,RequestCacheAwareFilter>>
  162. * SecurityContextHolderAwareRequestFilter
  163. * JaasApiIntegrationFilter
  164. * RememberMeAuthenticationFilter
  165. * AnonymousAuthenticationFilter
  166. * OAuth2AuthorizationCodeGrantFilter
  167. * SessionManagementFilter
  168. * <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>>
  169. * xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`]
  170. * SwitchUserFilter
  171. [[servlet-exceptiontranslationfilter]]
  172. == Handling Security Exceptions
  173. 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.
  174. `ExceptionTranslationFilter` is inserted into the <<servlet-filterchainproxy>> as one of the <<servlet-security-filters>>.
  175. image::{figures}/exceptiontranslationfilter.png[]
  176. * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application.
  177. * image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__.
  178. ** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out
  179. ** The `HttpServletRequest` is <<savedrequests,saved>> so that it can be used to replay the original request once authentication is successful.
  180. // FIXME: add link to authentication success
  181. ** The `AuthenticationEntryPoint` is used to request credentials from the client.
  182. For example, it might redirect to a log in page or send a `WWW-Authenticate` header.
  183. // FIXME: link to AuthenticationEntryPoint
  184. * image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__.
  185. The `AccessDeniedHandler` is invoked to handle access denied.
  186. // FIXME: link to AccessDeniedHandler
  187. [NOTE]
  188. ====
  189. If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything.
  190. ====
  191. The pseudocode for `ExceptionTranslationFilter` looks something like this:
  192. .ExceptionTranslationFilter pseudocode
  193. [source,java]
  194. ----
  195. try {
  196. filterChain.doFilter(request, response); // <1>
  197. } catch (AccessDeniedException | AuthenticationException ex) {
  198. if (!authenticated || ex instanceof AuthenticationException) {
  199. startAuthentication(); // <2>
  200. } else {
  201. accessDenied(); // <3>
  202. }
  203. }
  204. ----
  205. <1> You will recall from <<servlet-filters-review>> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application.
  206. 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.
  207. <2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__.
  208. <3> Otherwise, __Access Denied__
  209. [[savedrequests]]
  210. == Saving Requests Between Authentication
  211. As illustrated in <<servlet-exceptiontranslationfilter>>, when a request has no authentication and is for a resource that requires authentication, there is a need to save the request for the authenticated resource to re-request after authentication is successful.
  212. In Spring Security this is done by saving the `HttpServletRequest` using a <<requestcache,`RequestCache`>> implementation.
  213. [[requestcache]]
  214. === RequestCache
  215. The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`].
  216. When the user successfully authenticates, the `RequestCache` is used to replay the original request.
  217. The <<requestcacheawarefilter,`RequestCacheAwareFilter`>> is what uses the `RequestCache` to save the `HttpServletRequest`.
  218. By default, an `HttpSessionRequestCache` is used.
  219. The code below demonstrates how to customize the `RequestCache` implementation that is used to check the `HttpSession` for a saved request if the parameter named `continue` is present.
  220. include::partial$servlet/architecture/request-cache-continue.adoc[]
  221. [[requestcache-prevent-saved-request]]
  222. ==== Prevent the Request From Being Saved
  223. There are a number of reasons you may want to not store the user's unauthenticated request in the session.
  224. You may want to offload that storage onto the user's browser or store it in a database.
  225. Or you may want to shut off this feature since you always want to redirect the user to the home page instead of the page they tried to visit before login.
  226. To do that, you can use {security-api-url}org/springframework/security/web/savedrequest/NullRequestCache.html[the `NullRequestCache` implementation].
  227. .Prevent the Request From Being Saved
  228. [tabs]
  229. ======
  230. Java::
  231. +
  232. [source,java,role="primary"]
  233. ----
  234. @Bean
  235. SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  236. RequestCache nullRequestCache = new NullRequestCache();
  237. http
  238. // ...
  239. .requestCache((cache) -> cache
  240. .requestCache(nullRequestCache)
  241. );
  242. return http.build();
  243. }
  244. ----
  245. Kotlin::
  246. +
  247. [source,kotlin,role="secondary"]
  248. ----
  249. @Bean
  250. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  251. val nullRequestCache = NullRequestCache()
  252. http {
  253. requestCache {
  254. requestCache = nullRequestCache
  255. }
  256. }
  257. return http.build()
  258. }
  259. ----
  260. XML::
  261. +
  262. [source,xml,role="secondary"]
  263. ----
  264. <http auto-config="true">
  265. <!-- ... -->
  266. <request-cache ref="nullRequestCache"/>
  267. </http>
  268. <b:bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>
  269. ----
  270. ======
  271. [[requestcacheawarefilter]]
  272. === RequestCacheAwareFilter
  273. The {security-api-url}org/springframework/security/web/savedrequest/RequestCacheAwareFilter.html[`RequestCacheAwareFilter`] uses the <<requestcache,`RequestCache`>> to save the `HttpServletRequest`.