session-management.adoc 9.7 KB

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