persistence.adoc 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. [[persistant]]
  2. = Persisting Authentication
  3. :figures: servlet/authentication
  4. The first time a user requests a protected resource, they are xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[prompted for credentials].
  5. One of the most common ways to prompt for credentials is to redirect the user to a xref:servlet/authentication/passwords/form.adoc[log in page].
  6. A summarized HTTP exchange for an unauthenticated user requesting a protected resource might look like this:
  7. .Unauthenticated User Requests Protected Resource
  8. ====
  9. [source,http]
  10. ----
  11. GET / HTTP/1.1
  12. Host: example.com
  13. Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
  14. ----
  15. [source,http]
  16. ----
  17. HTTP/1.1 302 Found
  18. Location: /login
  19. ----
  20. ====
  21. The user submits their username and password.
  22. .Username and Password Submitted
  23. [source,http]
  24. ----
  25. POST /login HTTP/1.1
  26. Host: example.com
  27. Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
  28. username=user&password=password&_csrf=35942e65-a172-4cd4-a1d4-d16a51147b3e
  29. ----
  30. Upon authenticating the user, the user is associated to a new session id to prevent xref:servlet/authentication/session-management.adoc#ns-session-fixation[session fixation attacks].
  31. .Authenticated User is Associated to New Session
  32. [source,http]
  33. ----
  34. HTTP/1.1 302 Found
  35. Location: /
  36. Set-Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8; Path=/; HttpOnly; SameSite=Lax
  37. ----
  38. Subsequent requests include the session cookie which is used to authenticate the user for the remainder of the session.
  39. .Authenticated Session Provided as Credentials
  40. [source,http]
  41. ----
  42. GET / HTTP/1.1
  43. Host: example.com
  44. Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8
  45. ----
  46. [[securitycontextrepository]]
  47. == SecurityContextRepository
  48. // FIXME: api documentation
  49. In Spring Security the association of the user to future requests is made using {security-api-url}org/springframework/security/web/context/SecurityContextRepository.html[`SecurityContextRepository`].
  50. The default implementation of `SecurityContextRepository` is {security-api-url}org/springframework/security/web/context/DelegatingSecurityContextRepository.html[`DelegatingSecurityContextRepository`] which delegates to the following:
  51. * <<httpsecuritycontextrepository,`HttpSessionSecurityContextRepository`>>
  52. * <<requestattributesecuritycontextrepository,`RequestAttributeSecurityContextRepository`>>
  53. [[httpsecuritycontextrepository]]
  54. === HttpSessionSecurityContextRepository
  55. The {security-api-url}org/springframework/security/web/context/HttpSessionSecurityContextRepository.html[`HttpSessionSecurityContextRepository`] associates the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to the `HttpSession`.
  56. Users can replace `HttpSessionSecurityContextRepository` with another implementation of `SecurityContextRepository` if they wish to associate the user with subsequent requests in another way or not at all.
  57. [[nullsecuritycontextrepository]]
  58. === NullSecurityContextRepository
  59. If it is not desirable to associate the `SecurityContext` to an `HttpSession` (i.e. when authenticating with OAuth) the {security-api-url}org/springframework/security/web/context/NullSecurityContextRepository.html[`NullSecurityContextRepository`] is an implementation of `SecurityContextRepository` that does nothing.
  60. [[requestattributesecuritycontextrepository]]
  61. === RequestAttributeSecurityContextRepository
  62. The {security-api-url}org/springframework/security/web/context/RequestAttributeSecurityContextRepository.html[`RequestAttributeSecurityContextRepository`] saves the `SecurityContext` as a request attribute to make sure the `SecurityContext` is available for a single request that occurs across dispatch types that may clear out the `SecurityContext`.
  63. For example, assume that a client makes a request, is authenticated, and then an error occurs.
  64. Depending on the servlet container implementation, the error means that any `SecurityContext` that was established is cleared out and then the error dispatch is made.
  65. When the error dispatch is made, there is no `SecurityContext` established.
  66. This means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow.
  67. .Use RequestAttributeSecurityContextRepository
  68. [tabs]
  69. ======
  70. Java::
  71. +
  72. [source,java,role="primary"]
  73. ----
  74. public SecurityFilterChain filterChain(HttpSecurity http) {
  75. http
  76. // ...
  77. .securityContext((securityContext) -> securityContext
  78. .securityContextRepository(new RequestAttributeSecurityContextRepository())
  79. );
  80. return http.build();
  81. }
  82. ----
  83. XML::
  84. +
  85. [source,xml,role="secondary"]
  86. ----
  87. <http security-context-repository-ref="contextRepository">
  88. <!-- ... -->
  89. </http>
  90. <b:bean name="contextRepository"
  91. class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
  92. ----
  93. ======
  94. [[delegatingsecuritycontextrepository]]
  95. === DelegatingSecurityContextRepository
  96. The {security-api-url}org/springframework/security/web/context/DelegatingSecurityContextRepository.html[`DelegatingSecurityContextRepository`] saves the `SecurityContext` to multiple `SecurityContextRepository` delegates and allows retrieval from any of the delegates in a specified order.
  97. The most useful arrangement for this is configured with the following example, which allows the use of both xref:requestattributesecuritycontextrepository[`RequestAttributeSecurityContextRepository`] and xref:httpsecuritycontextrepository[`HttpSessionSecurityContextRepository`] simultaneously.
  98. .Configure DelegatingSecurityContextRepository
  99. [tabs]
  100. ======
  101. Java::
  102. +
  103. [source,java,role="primary"]
  104. ----
  105. @Bean
  106. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  107. http
  108. // ...
  109. .securityContext((securityContext) -> securityContext
  110. .securityContextRepository(new DelegatingSecurityContextRepository(
  111. new RequestAttributeSecurityContextRepository(),
  112. new HttpSessionSecurityContextRepository()
  113. ))
  114. );
  115. return http.build();
  116. }
  117. ----
  118. Kotlin::
  119. +
  120. [source,kotlin,role="secondary"]
  121. ----
  122. @Bean
  123. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  124. http {
  125. // ...
  126. securityContext {
  127. securityContextRepository = DelegatingSecurityContextRepository(
  128. RequestAttributeSecurityContextRepository(),
  129. HttpSessionSecurityContextRepository()
  130. )
  131. }
  132. }
  133. return http.build()
  134. }
  135. ----
  136. XML::
  137. +
  138. [source,xml,role="secondary"]
  139. ----
  140. <http security-context-repository-ref="contextRepository">
  141. <!-- ... -->
  142. </http>
  143. <bean name="contextRepository"
  144. class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
  145. <constructor-arg>
  146. <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
  147. </constructor-arg>
  148. <constructor-arg>
  149. <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
  150. </constructor-arg>
  151. </bean>
  152. ----
  153. ======
  154. [NOTE]
  155. ====
  156. In Spring Security 6, the example shown above is the default configuration.
  157. ====
  158. [[securitycontextpersistencefilter]]
  159. == SecurityContextPersistenceFilter
  160. The {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
  161. image::{figures}/securitycontextpersistencefilter.png[]
  162. image:{icondir}/number_1.png[] Before running the rest of the application, `SecurityContextPersistenceFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.
  163. image:{icondir}/number_2.png[] Next, the application is ran.
  164. image:{icondir}/number_3.png[] Finally, if the `SecurityContext` has changed, we save the `SecurityContext` using the `SecurityContextPersistenceRepository`.
  165. This means that when using `SecurityContextPersistenceFilter`, just setting the `SecurityContextHolder` will ensure that the `SecurityContext` is persisted using `SecurityContextRepository`.
  166. In some cases a response is committed and written to the client before the `SecurityContextPersistenceFilter` method completes.
  167. For example, if a redirect is sent to the client the response is immediately written back to the client.
  168. This means that establishing an `HttpSession` would not be possible in step 3 because the session id could not be included in the already written response.
  169. Another situation that can happen is that if a client authenticates successfully, the response is committed before `SecurityContextPersistenceFilter` completes, and the client makes a second request before the `SecurityContextPersistenceFilter` completes the wrong authentication could be present in the second request.
  170. To avoid these problems, the `SecurityContextPersistenceFilter` wraps both the `HttpServletRequest` and the `HttpServletResponse` to detect if the `SecurityContext` has changed and if so save the `SecurityContext` just before the response is committed.
  171. [[securitycontextholderfilter]]
  172. == SecurityContextHolderFilter
  173. The {security-api-url}org/springframework/security/web/context/SecurityContextHolderFilter.html[`SecurityContextHolderFilter`] is responsible for loading the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
  174. image::{figures}/securitycontextholderfilter.png[]
  175. image:{icondir}/number_1.png[] Before running the rest of the application, `SecurityContextHolderFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.
  176. image:{icondir}/number_2.png[] Next, the application is ran.
  177. Unlike, xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`], `SecurityContextHolderFilter` only loads the `SecurityContext` it does not save the `SecurityContext`.
  178. This means that when using `SecurityContextHolderFilter`, it is required that the `SecurityContext` is explicitly saved.
  179. include::partial$servlet/architecture/security-context-explicit.adoc[]