429 lines
15 KiB
Plaintext
Raw Normal View History

2022-10-24 17:38:58 -06:00
[[migration]]
= Migrating to 6.0
The Spring Security team has prepared the 5.8 release to simplify upgrading to Spring Security 6.0.
Use 5.8 and
ifdef::spring-security-version[]
xref:5.8.0@migration.adoc[its preparation steps]
endif::[]
ifndef::spring-security-version[]
its preparation steps
endif::[]
to simplify updating to 6.0
2022-10-24 17:38:58 -06:00
2022-10-25 15:04:43 -06:00
After updating to 5.8, follow this guide to perform any needed migration steps.
2022-10-24 17:38:58 -06:00
2022-10-26 12:52:37 -06:00
Also, this guide includes ways to <<revert,revert to 5.x>> behaviors and its defaults, should you run into trouble.
2022-10-24 17:38:58 -06:00
2022-10-26 12:52:37 -06:00
== Servlet
2022-10-24 17:38:58 -06:00
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.
2022-10-27 15:39:03 -05:00
If you are explicitly opting into Spring Security 6's new defaults, the following configuration can be removed to accept the Spring Security 6 defaults.
include::partial$servlet/architecture/security-context-explicit.adoc[]
[[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[]
2022-10-26 12:52:37 -06:00
=== Use `AuthorizationManager` for Method Security
2022-10-24 17:38:58 -06:00
2022-10-26 12:52:37 -06:00
There are no further migration steps for this feature.
=== Use `AuthorizationManager` for Message Security
In 6.0, `<websocket-message-broker>` defaults `use-authorization-manager` to `true`.
So, to complete migration, remove any `websocket-message-broker@use-authorization-manager=true` attribute.
For example:
====
.Xml
[source,xml,role="primary"]
----
<websocket-message-broker use-authorization-manager="true"/>
----
====
changes to:
====
.Xml
[source,xml,role="primary"]
----
<websocket-message-broker/>
----
====
There are no further migrations steps for Java or Kotlin for this feature.
=== Use `AuthorizationManager` for Request Security
In 6.0, `<http>` defaults `once-per-request` to `false`, `filter-all-dispatcher-types` to `true`, and `use-authorization-manager` to `true`.
Also, xref:servlet/authorization/authorize-requests.adoc#filtersecurityinterceptor-every-request[`authorizeRequests#filterSecurityInterceptorOncePerRequest`] defaults to `false` and xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests#filterAllDispatcherTypes`] defaults to `true`.
So, to complete migration, any defaults values can be removed.
For example, if you opted in to the 6.0 default for `filter-all-dispatcher-types` or `authorizeHttpRequests#filterAllDispatcherTypes` like so:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.filterAllDispatcherTypes(true)
// ...
)
----
.Kotlin
[source,java,role="secondary"]
----
http {
authorizeHttpRequests {
filterAllDispatcherTypes = true
// ...
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http use-authorization-manager="true" filter-all-dispatcher-types="true"/>
----
====
then the defaults may be removed:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
----
.Kotlin
[source,java,role="secondary"]
----
http {
authorizeHttpRequests {
// ...
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http/>
----
====
[NOTE]
====
`once-per-request` applies only when `use-authorization-manager="false"` and `filter-all-dispatcher-types` only applies when `use-authorization-manager="true"`
====
=== Propagate ``AuthenticationServiceException``s
{security-api-url}org/springframework/security/web/authentication/AuthenticationFilter.html[`AuthenticationFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/authentication/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`].
Because ``AuthenticationServiceException``s represent a server-side error instead of a client-side error, in 6.0, this changes to propagate them to the container.
So, if you opted into this behavior by setting `rethrowAuthenticationServiceException` too `true`, you can now remove it like so:
====
.Java
[source,java,role="primary"]
----
AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
handler.setRethrowAuthenticationServiceException(true);
authenticationFilter.setAuthenticationFailureHandler(handler);
----
.Kotlin
[source,kotlin,role="secondary"]
----
val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
handler.setRethrowAuthenticationServiceException(true)
authenticationFilter.setAuthenticationFailureHandler(handler)
----
.Xml
[source,xml,role="secondary"]
----
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
<!-- ... -->
<property ref="authenticationFailureHandler"/>
</bean>
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
<property name="rethrowAuthenticationServiceException" value="true"/>
</bean>
----
====
changes to:
====
.Java
[source,java,role="primary"]
----
AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
authenticationFilter.setAuthenticationFailureHandler(handler);
----
.Kotlin
[source,kotlin,role="secondary"]
----
val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
authenticationFilter.setAuthenticationFailureHandler(handler)
----
.Xml
[source,xml,role="secondary"]
----
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.AuthenticationFilter">
<!-- ... -->
<property ref="authenticationFailureHandler"/>
</bean>
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler">
<!-- ... -->
</bean>
----
====
[[servlet-opt-in-sha256-rememberme]]
=== Use SHA-256 in Remember Me
The `TokenBasedRememberMeServices` implementation now supports SHA-256 for the Remember Me token and this is the default in Spring Security 6.
This change makes the implementation more secure by default since MD5 is already proven to be a weak hashing algorithm and vulnerable against collision attacks and modular differential attacks.
The new generated tokens now have the information of which algorithm was used to generate the token and that information is used in order to match it.
If the algorithm name is not present, then the `matchingAlgorithm` property is used to check the token.
This allows for a smooth transition from MD5 to SHA-256.
To opt into the new Spring Security 6 default to encode the tokens while still being able to decode tokens encoded with MD5, you can set the `encodingAlgorithm` property to SHA-256 and the `matchingAlgorithm` property to MD5.
See the xref:servlet/authentication/rememberme.adoc#_tokenbasedremembermeservices[reference documentation] and the {security-api-url}org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.html[API docs] for more information.
[[servlet-opt-in-sha256-sha256-encoding]]
.Use Spring Security 6 defaults for encoding, SHA-256 for encoding and MD5 for matching
====
.Java
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
http
// ...
.rememberMe((remember) -> remember
.rememberMeServices(rememberMeServices)
);
return http.build();
}
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
return rememberMe;
}
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<remember-me services-ref="rememberMeServices"/>
</http>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
<property name="matchingAlgorithm" value="MD5"/>
<property name="encodingAlgorithm" value="SHA256"/>
</bean>
----
====
At some point, you will want to fully migrate to Spring Security 6 defaults. But how do you know when it is safe to do so?
Let's suppose that you deployed your application using SHA-256 as the encoding algorithm (as you have done <<servlet-opt-in-sha256-sha256-encoding,here>>) on November 1st, if you have the value for the `tokenValiditySeconds` property set to N days (14 is the default), you can migrate to SHA-256 N days after November 1st (which is November 15th in this example).
By that time, all the tokens generated with MD5 will have expired.
.Use Spring Security 6 defaults, SHA-256 for both encoding and matching
====
.Java
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
http
// ...
.rememberMe((remember) -> remember
.rememberMeServices(rememberMeServices)
);
return http.build();
}
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
return rememberMe;
}
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<remember-me services-ref="rememberMeServices"/>
</http>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
<property name="matchingAlgorithm" value="SHA256"/>
<property name="encodingAlgorithm" value="SHA256"/>
</bean>
----
====
If you are having problems with the Spring Security 6 defaults, you can explicitly opt into 5.8 defaults using the following configuration:
.Use MD5 for both encoding and matching algorithms
====
.Java
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
http
// ...
.rememberMe((remember) -> remember
.rememberMeServices(rememberMeServices)
);
return http.build();
}
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.MD5;
TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
return rememberMe;
}
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<remember-me services-ref="rememberMeServices"/>
</http>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
<property name="matchingAlgorithm" value="MD5"/>
<property name="encodingAlgorithm" value="MD5"/>
</bean>
----
====
2022-10-26 12:52:37 -06:00
== Reactive
=== Use `AuthorizationManager` for Method Security
In 6.0, `@EnableReactiveMethodSecurity` defaults `useAuthorizationManager` to `true`.
So, to complete migration, {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurity.html[`@EnableReactiveMethodSecurity`] remove the `useAuthorizationManager` attribute:
2022-10-24 17:38:58 -06:00
====
.Java
[source,java,role="primary"]
----
2022-10-25 15:04:43 -06:00
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
2022-10-24 17:38:58 -06:00
----
.Kotlin
[source,kotlin,role="secondary"]
----
2022-10-25 15:04:43 -06:00
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
2022-10-24 17:38:58 -06:00
----
====
changes to:
2022-10-24 17:38:58 -06:00
====
.Java
[source,java,role="primary"]
----
2022-10-25 15:04:43 -06:00
@EnableReactiveMethodSecurity
2022-10-24 17:38:58 -06:00
----
.Kotlin
[source,kotlin,role="secondary"]
----
2022-10-25 15:04:43 -06:00
@EnableReactiveMethodSecurity
2022-10-24 17:38:58 -06:00
----
====
2022-10-26 12:52:37 -06:00
'''
2022-10-25 15:04:43 -06:00
2022-10-26 12:52:37 -06:00
[[revert]]
2022-10-25 15:04:43 -06:00
If you are running into trouble with any of the 6.0 changes, please first try to apply the following changes to get you up and running.
It's more important to stay on 6.0 and get the security improvements.
2022-10-26 12:52:37 -06:00
== Revert Servlet
== Revert Reactive