2
0

session-management.adoc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. = Session Management Migrations
  2. == Require Explicit Saving of SecurityContextRepository
  3. In Spring Security 5, the default behavior is for the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to automatically be saved to the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] using the xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`].
  4. Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.
  5. Unfortunately, automatic persistence of the `SecurityContext` can surprise users when it is done prior to the request completing (i.e. just prior to committing the `HttpServletResponse`).
  6. It also is complex to keep track of the state to determine if a save is necessary causing unnecessary writes to the `SecurityContextRepository` (i.e. `HttpSession`) at times.
  7. In Spring Security 6, the default behavior is that the xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[`SecurityContextHolderFilter`] will only read the `SecurityContext` from `SecurityContextRepository` and populate it in the `SecurityContextHolder`.
  8. Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.
  9. This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.
  10. [NOTE]
  11. ====
  12. Saving the context is also needed when clearing it out, for example during logout. Refer to this section to xref:servlet/authentication/session-management.adoc#properly-clearing-authentication[know more about that].
  13. ====
  14. To opt into the new Spring Security 6 default, the following configuration can be used.
  15. include::partial$servlet/architecture/security-context-explicit.adoc[]
  16. == Change `HttpSessionSecurityContextRepository` to `DelegatingSecurityContextRepository`
  17. In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] is `HttpSessionSecurityContextRepository`.
  18. In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`.
  19. To opt into the new Spring Security 6 default, the following configuration can be used.
  20. .Configure SecurityContextRepository with 6.0 defaults
  21. ====
  22. .Java
  23. [source,java,role="primary"]
  24. ----
  25. @Bean
  26. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  27. http
  28. // ...
  29. .securityContext((securityContext) -> securityContext
  30. .securityContextRepository(new DelegatingSecurityContextRepository(
  31. new RequestAttributeSecurityContextRepository(),
  32. new HttpSessionSecurityContextRepository()
  33. ))
  34. );
  35. return http.build();
  36. }
  37. ----
  38. .Kotlin
  39. [source,kotlin,role="secondary"]
  40. ----
  41. @Bean
  42. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  43. http {
  44. // ...
  45. securityContext {
  46. securityContextRepository = DelegatingSecurityContextRepository(
  47. RequestAttributeSecurityContextRepository(),
  48. HttpSessionSecurityContextRepository()
  49. )
  50. }
  51. }
  52. return http.build()
  53. }
  54. ----
  55. .XML
  56. [source,xml,role="secondary"]
  57. ----
  58. <http security-context-repository-ref="contextRepository">
  59. <!-- ... -->
  60. </http>
  61. <bean name="contextRepository"
  62. class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
  63. <constructor-arg>
  64. <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
  65. </constructor-arg>
  66. <constructor-arg>
  67. <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
  68. </constructor-arg>
  69. </bean>
  70. ----
  71. ====
  72. [IMPORTANT]
  73. ====
  74. If you are already using an implementation other than `HttpSessionSecurityContextRepository`, you should replace it with your chosen implementation in the example above to ensure that it is used along with `RequestAttributeSecurityContextRepository`.
  75. ====
  76. == Address `SecurityContextRepository` Deprecations
  77. In Spring Security 5.7, a new method was added to xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] with the signature:
  78. Supplier<SecurityContext> loadContext(HttpServletRequest request)
  79. With the addition of xref:servlet/authentication/persistence.adoc#delegatingsecuritycontextrepository[`DelegatingSecurityContextRepository`] in Spring Security 5.8, that method was deprecated in favor of a new method with the signature:
  80. DeferredSecurityContext loadDeferredContext(HttpServletRequest request)
  81. In Spring Security 6, the deprecated method was removed.
  82. If you have implemented `SecurityContextRepository` yourself and added an implementation of the `loadContext(request)` method, you can prepare for Spring Security 6 by removing the implementation of that method and implementing the new method instead.
  83. To get started implementing the new method, use the following example to provide a `DeferredSecurityContext`:
  84. .Provide `DeferredSecurityContext`
  85. ====
  86. .Java
  87. [source,java,role="primary"]
  88. ----
  89. @Override
  90. public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
  91. return new DeferredSecurityContext() {
  92. private SecurityContext securityContext;
  93. private boolean isGenerated;
  94. @Override
  95. public SecurityContext get() {
  96. if (this.securityContext == null) {
  97. this.securityContext = getContextOrNull(request);
  98. if (this.securityContext == null) {
  99. SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy();
  100. this.securityContext = strategy.createEmptyContext();
  101. this.isGenerated = true;
  102. }
  103. }
  104. return this.securityContext;
  105. }
  106. @Override
  107. public boolean isGenerated() {
  108. get();
  109. return this.isGenerated;
  110. }
  111. };
  112. }
  113. ----
  114. .Kotlin
  115. [source,kotlin,role="secondary"]
  116. ----
  117. override fun loadDeferredContext(request: HttpServletRequest): DeferredSecurityContext {
  118. return object : DeferredSecurityContext {
  119. private var securityContext: SecurityContext? = null
  120. private var isGenerated = false
  121. override fun get(): SecurityContext {
  122. if (securityContext == null) {
  123. securityContext = getContextOrNull(request)
  124. ?: SecurityContextHolder.getContextHolderStrategy().createEmptyContext()
  125. .also { isGenerated = true }
  126. }
  127. return securityContext!!
  128. }
  129. override fun isGenerated(): Boolean {
  130. get()
  131. return isGenerated
  132. }
  133. }
  134. }
  135. ----
  136. ====
  137. [[requestcache-query-optimization]]
  138. == Optimize Querying of `RequestCache`
  139. In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
  140. This means that in a typical setup, that in order to use the xref:servlet/architecture.adoc#requestcache[`RequestCache`] the `HttpSession` is queried on every request.
  141. In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
  142. This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
  143. In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
  144. If you are not overriding the defaults (i.e. using `NullRequestCache`), then the following configuration can be used to explicitly opt into the Spring Security 6 behavior in Spring Security 5.8:
  145. include::partial$servlet/architecture/request-cache-continue.adoc[]
  146. == Require Explicit Invocation of SessionAuthenticationStrategy
  147. In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the `SessionAuthenticationStrategy`.
  148. The problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.
  149. In Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.
  150. This means that there is no need to detect when `Authentication` is done and thus the `HttpSession` does not need to be read for every request.
  151. To opt into the new Spring Security 6 default, the following configuration can be used.
  152. .Require Explicit `SessionAuthenticationStrategy` Invocation
  153. ====
  154. .Java
  155. [source,java,role="primary"]
  156. ----
  157. @Bean
  158. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  159. http
  160. // ...
  161. .sessionManagement((sessions) -> sessions
  162. .requireExplicitAuthenticationStrategy(true)
  163. );
  164. return http.build();
  165. }
  166. ----
  167. .Kotlin
  168. [source,kotlin,role="secondary"]
  169. ----
  170. @Bean
  171. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  172. http {
  173. sessionManagement {
  174. requireExplicitAuthenticationStrategy = true
  175. }
  176. }
  177. return http.build()
  178. }
  179. ----
  180. .XML
  181. [source,xml,role="secondary"]
  182. ----
  183. <http>
  184. <!-- ... -->
  185. <session-management authentication-strategy-explicit-invocation="true"/>
  186. </http>
  187. ----
  188. ====
  189. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  190. .Explicit use Spring Security 5.8 defaults for `SessionAuthenticationStrategy`
  191. ====
  192. .Java
  193. [source,java,role="primary"]
  194. ----
  195. @Bean
  196. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  197. http
  198. // ...
  199. .sessionManagement((sessions) -> sessions
  200. .requireExplicitAuthenticationStrategy(false)
  201. );
  202. return http.build();
  203. }
  204. ----
  205. .Kotlin
  206. [source,kotlin,role="secondary"]
  207. ----
  208. @Bean
  209. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  210. http {
  211. sessionManagement {
  212. requireExplicitAuthenticationStrategy = false
  213. }
  214. }
  215. return http.build()
  216. }
  217. ----
  218. .XML
  219. [source,xml,role="secondary"]
  220. ----
  221. <http>
  222. <!-- ... -->
  223. <session-management authentication-strategy-explicit-invocation="false"/>
  224. </http>
  225. ----
  226. ====