123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- = Session Management Migrations
- == Require Explicit Saving of SecurityContextRepository
- 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`].
- Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.
- 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`).
- 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.
- 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`.
- Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.
- This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.
- [NOTE]
- ====
- 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].
- ====
- To opt into the new Spring Security 6 default, the following configuration can be used.
- include::partial$servlet/architecture/security-context-explicit.adoc[]
- == Change `HttpSessionSecurityContextRepository` to `DelegatingSecurityContextRepository`
- In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] is `HttpSessionSecurityContextRepository`.
- In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`.
- To opt into the new Spring Security 6 default, the following configuration can be used.
- .Configure SecurityContextRepository with 6.0 defaults
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Bean
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .securityContext((securityContext) -> securityContext
- .securityContextRepository(new DelegatingSecurityContextRepository(
- new RequestAttributeSecurityContextRepository(),
- new HttpSessionSecurityContextRepository()
- ))
- );
- return http.build();
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Bean
- fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- securityContext {
- securityContextRepository = DelegatingSecurityContextRepository(
- RequestAttributeSecurityContextRepository(),
- HttpSessionSecurityContextRepository()
- )
- }
- }
- return http.build()
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http security-context-repository-ref="contextRepository">
- <!-- ... -->
- </http>
- <bean name="contextRepository"
- class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
- <constructor-arg>
- <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
- </constructor-arg>
- <constructor-arg>
- <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
- </constructor-arg>
- </bean>
- ----
- ======
- [IMPORTANT]
- ====
- 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`.
- ====
- == Address `SecurityContextRepository` Deprecations
- In Spring Security 5.7, a new method was added to xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] with the signature:
- Supplier<SecurityContext> loadContext(HttpServletRequest request)
- 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:
- DeferredSecurityContext loadDeferredContext(HttpServletRequest request)
- In Spring Security 6, the deprecated method was removed.
- 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.
- To get started implementing the new method, use the following example to provide a `DeferredSecurityContext`:
- .Provide `DeferredSecurityContext`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Override
- public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
- return new DeferredSecurityContext() {
- private SecurityContext securityContext;
- private boolean isGenerated;
- @Override
- public SecurityContext get() {
- if (this.securityContext == null) {
- this.securityContext = getContextOrNull(request);
- if (this.securityContext == null) {
- SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy();
- this.securityContext = strategy.createEmptyContext();
- this.isGenerated = true;
- }
- }
- return this.securityContext;
- }
- @Override
- public boolean isGenerated() {
- get();
- return this.isGenerated;
- }
- };
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- override fun loadDeferredContext(request: HttpServletRequest): DeferredSecurityContext {
- return object : DeferredSecurityContext {
- private var securityContext: SecurityContext? = null
- private var isGenerated = false
- override fun get(): SecurityContext {
- if (securityContext == null) {
- securityContext = getContextOrNull(request)
- ?: SecurityContextHolder.getContextHolderStrategy().createEmptyContext()
- .also { isGenerated = true }
- }
- return securityContext!!
- }
- override fun isGenerated(): Boolean {
- get()
- return isGenerated
- }
- }
- }
- ----
- ======
- [[requestcache-query-optimization]]
- == Optimize Querying of `RequestCache`
- In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request.
- 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.
- In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined.
- This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`.
- In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request.
- 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:
- include::partial$servlet/architecture/request-cache-continue.adoc[]
- == Require Explicit Invocation of SessionAuthenticationStrategy
- In Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the `SessionAuthenticationStrategy`.
- The problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.
- In Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.
- 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.
- To opt into the new Spring Security 6 default, the following configuration can be used.
- .Require Explicit `SessionAuthenticationStrategy` Invocation
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Bean
- DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
- http
- // ...
- .sessionManagement((sessions) -> sessions
- .requireExplicitAuthenticationStrategy(true)
- );
- return http.build();
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
- http {
- sessionManagement {
- requireExplicitAuthenticationStrategy = true
- }
- }
- return http.build()
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <session-management authentication-strategy-explicit-invocation="true"/>
- </http>
- ----
- ======
- If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
- .Explicit use Spring Security 5.8 defaults for `SessionAuthenticationStrategy`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Bean
- DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
- http
- // ...
- .sessionManagement((sessions) -> sessions
- .requireExplicitAuthenticationStrategy(false)
- );
- return http.build();
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
- http {
- sessionManagement {
- requireExplicitAuthenticationStrategy = false
- }
- }
- return http.build()
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <session-management authentication-strategy-explicit-invocation="false"/>
- </http>
- ----
- ======
|