session-management.adoc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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. To opt into the new Spring Security 6 default, the following configuration can be used.
  11. include::partial$servlet/architecture/security-context-explicit.adoc[]
  12. == Change `HttpSessionSecurityContextRepository` to `DelegatingSecurityContextRepository`
  13. In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] is `HttpSessionSecurityContextRepository`.
  14. In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`.
  15. To opt into the new Spring Security 6 default, the following configuration can be used.
  16. .Configure SecurityContextRepository with 6.0 defaults
  17. ====
  18. .Java
  19. [source,java,role="primary"]
  20. ----
  21. @Bean
  22. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  23. http
  24. // ...
  25. .securityContext((securityContext) -> securityContext
  26. .securityContextRepository(new DelegatingSecurityContextRepository(
  27. new RequestAttributeSecurityContextRepository(),
  28. new HttpSessionSecurityContextRepository()
  29. ))
  30. );
  31. return http.build();
  32. }
  33. ----
  34. .Kotlin
  35. [source,kotlin,role="secondary"]
  36. ----
  37. @Bean
  38. fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  39. http {
  40. // ...
  41. securityContext {
  42. securityContextRepository = DelegatingSecurityContextRepository(
  43. RequestAttributeSecurityContextRepository(),
  44. HttpSessionSecurityContextRepository()
  45. )
  46. }
  47. }
  48. return http.build()
  49. }
  50. ----
  51. .XML
  52. [source,xml,role="secondary"]
  53. ----
  54. <http security-context-repository-ref="contextRepository">
  55. <!-- ... -->
  56. </http>
  57. <bean name="contextRepository"
  58. class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
  59. <constructor-arg>
  60. <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
  61. </constructor-arg>
  62. <constructor-arg>
  63. <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
  64. </constructor-arg>
  65. </bean>
  66. ----
  67. ====
  68. [IMPORTANT]
  69. ====
  70. 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`.
  71. ====
  72. == Address `SecurityContextRepository` Deprecations
  73. In Spring Security 5.7, a new method was added to xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] with the signature:
  74. Supplier<SecurityContext> loadContext(HttpServletRequest request)
  75. 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:
  76. DeferredSecurityContext loadDeferredContext(HttpServletRequest request)
  77. In Spring Security 6, the deprecated method was removed.
  78. 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.
  79. To get started implementing the new method, use the following example to provide a `DeferredSecurityContext`:
  80. .Provide `DeferredSecurityContext`
  81. ====
  82. .Java
  83. [source,java,role="primary"]
  84. ----
  85. @Override
  86. public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
  87. return new DeferredSecurityContext() {
  88. private SecurityContext securityContext;
  89. private boolean isGenerated;
  90. @Override
  91. public SecurityContext get() {
  92. if (this.securityContext == null) {
  93. this.securityContext = getContextOrNull(request);
  94. if (this.securityContext == null) {
  95. SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy();
  96. this.securityContext = strategy.createEmptyContext();
  97. this.isGenerated = true;
  98. }
  99. }
  100. return this.securityContext;
  101. }
  102. @Override
  103. public boolean isGenerated() {
  104. get();
  105. return this.isGenerated;
  106. }
  107. };
  108. }
  109. ----
  110. .Kotlin
  111. [source,kotlin,role="secondary"]
  112. ----
  113. override fun loadDeferredContext(request: HttpServletRequest): DeferredSecurityContext {
  114. return object : DeferredSecurityContext {
  115. private var securityContext: SecurityContext? = null
  116. private var isGenerated = false
  117. override fun get(): SecurityContext {
  118. if (securityContext == null) {
  119. securityContext = getContextOrNull(request)
  120. ?: SecurityContextHolder.getContextHolderStrategy().createEmptyContext()
  121. .also { isGenerated = true }
  122. }
  123. return securityContext!!
  124. }
  125. override fun isGenerated(): Boolean {
  126. get()
  127. return isGenerated
  128. }
  129. }
  130. }
  131. ----
  132. ====
  133. [[requestcache-query-optimization]]
  134. == Optimize Querying of `RequestCache`
  135. In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
  136. 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.
  137. In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
  138. This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
  139. In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
  140. 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:
  141. include::partial$servlet/architecture/request-cache-continue.adoc[]
  142. == Require Explicit Invocation of SessionAuthenticationStrategy
  143. In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the `SessionAuthenticationStrategy`.
  144. The problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.
  145. In Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.
  146. 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.
  147. To opt into the new Spring Security 6 default, the following configuration can be used.
  148. .Require Explicit `SessionAuthenticationStrategy` Invocation
  149. ====
  150. .Java
  151. [source,java,role="primary"]
  152. ----
  153. @Bean
  154. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  155. http
  156. // ...
  157. .sessionManagement((sessions) -> sessions
  158. .requireExplicitAuthenticationStrategy(true)
  159. );
  160. return http.build();
  161. }
  162. ----
  163. .Kotlin
  164. [source,kotlin,role="secondary"]
  165. ----
  166. @Bean
  167. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  168. http {
  169. sessionManagement {
  170. requireExplicitAuthenticationStrategy = true
  171. }
  172. }
  173. return http.build()
  174. }
  175. ----
  176. .XML
  177. [source,xml,role="secondary"]
  178. ----
  179. <http>
  180. <!-- ... -->
  181. <session-management authentication-strategy-explicit-invocation="true"/>
  182. </http>
  183. ----
  184. ====
  185. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  186. .Explicit use Spring Security 5.8 defaults for `SessionAuthenticationStrategy`
  187. ====
  188. .Java
  189. [source,java,role="primary"]
  190. ----
  191. @Bean
  192. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  193. http
  194. // ...
  195. .sessionManagement((sessions) -> sessions
  196. .requireExplicitAuthenticationStrategy(false)
  197. );
  198. return http.build();
  199. }
  200. ----
  201. .Kotlin
  202. [source,kotlin,role="secondary"]
  203. ----
  204. @Bean
  205. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  206. http {
  207. sessionManagement {
  208. requireExplicitAuthenticationStrategy = false
  209. }
  210. }
  211. return http.build()
  212. }
  213. ----
  214. .XML
  215. [source,xml,role="secondary"]
  216. ----
  217. <http>
  218. <!-- ... -->
  219. <session-management authentication-strategy-explicit-invocation="false"/>
  220. </http>
  221. ----
  222. ====