For the latest stable version, please use Spring Security 6.4.2! |
Reactive Migrations
If you have already performed the initial migration steps for your Reactive application, you’re now ready to perform steps specific to Reactive applications.
Exploit Protection Migrations
The following steps relate to changes around how to configure CSRF.
Configure tokenFromMultipartDataEnabled
In Spring Security 5.8, the method tokenFromMultipartDataEnabled
was deprecated in favor of ServerCsrfTokenRequestAttributeHandler#setTokenFromMultipartDataEnabled
.
To address the deprecation, the following code:
tokenFromMultipartDataEnabled
with DSL-
Java
-
Kotlin
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf((csrf) -> csrf
.tokenFromMultipartDataEnabled(true)
);
return http.build();
}
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
tokenFromMultipartDataEnabled = true
}
}
}
can be replaced with:
tokenFromMultipartDataEnabled
with ServerCsrfTokenRequestAttributeHandler
-
Java
-
Kotlin
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
requestHandler.setTokenFromMultipartDataEnabled(true);
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val requestHandler = ServerCsrfTokenRequestAttributeHandler()
requestHandler.tokenFromMultipartDataEnabled = true
return http {
// ...
csrf {
csrfTokenRequestHandler = requestHandler
}
}
}
Protect against CSRF BREACH
You can opt into Spring Security 6’s default support for BREACH protection of the CsrfToken
using the following configuration:
CsrfToken
BREACH Protection-
Java
-
Kotlin
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
XorServerCsrfTokenRequestAttributeHandler requestHandler = new XorServerCsrfTokenRequestAttributeHandler();
// ...
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val requestHandler = XorServerCsrfTokenRequestAttributeHandler()
// ...
return http {
// ...
csrf {
csrfTokenRequestHandler = requestHandler
}
}
}
Opt-out Steps
If configuring CSRF BREACH protection gives you trouble, take a look at these scenarios for optimal opt out behavior:
I am using AngularJS or another Javascript framework
If you are using AngularJS and the HttpClientXsrfModule (or a similar module in another framework) along with CookieServerCsrfTokenRepository.withHttpOnlyFalse()
, you may find that automatic support no longer works.
In this case, you can configure Spring Security to validate the raw CsrfToken
from the cookie while keeping CSRF BREACH protection of the response using a custom ServerCsrfTokenRequestHandler
with delegation, like so:
CsrfToken
BREACH Protection to validate raw tokens-
Java
-
Kotlin
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
CookieServerCsrfTokenRepository tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse();
XorServerCsrfTokenRequestAttributeHandler delegate = new XorServerCsrfTokenRequestAttributeHandler();
// Use only the handle() method of XorServerCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from ServerCsrfTokenRequestHandler
ServerCsrfTokenRequestHandler requestHandler = delegate::handle;
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
WebFilter csrfCookieWebFilter() {
return (exchange, chain) -> {
Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
return csrfToken.doOnSuccess(token -> {
/* Ensures the token is subscribed to. */
}).then(chain.filter(exchange));
};
}
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
val delegate = XorServerCsrfTokenRequestAttributeHandler()
// Use only the handle() method of XorServerCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from ServerCsrfTokenRequestHandler
val requestHandler = ServerCsrfTokenRequestHandler(delegate::handle)
return http.invoke {
// ...
csrf {
csrfTokenRepository = tokenRepository
csrfTokenRequestHandler = requestHandler
}
}
}
@Bean
fun csrfCookieWebFilter(): WebFilter {
return WebFilter { exchange, chain ->
val csrfToken = exchange.getAttribute<Mono<CsrfToken>>(CsrfToken::class.java.name) ?: Mono.empty()
csrfToken.doOnSuccess {
/* Ensures the token is subscribed to. */
}.then(chain.filter(exchange))
}
}
I need to opt out of CSRF BREACH protection for another reason
If CSRF BREACH protection does not work for you for another reason, you can opt out using the following configuration:
CsrfToken
BREACH protection-
Java
-
Kotlin
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val requestHandler = ServerCsrfTokenRequestAttributeHandler()
return http {
// ...
csrf {
csrfTokenRequestHandler = requestHandler
}
}
}
Use AuthorizationManager
for Method Security
Method Security has been improved through the AuthorizationManager
API and direct use of Spring AOP.
Should you run into trouble with making these changes, you can follow the opt out steps at the end of this section.
In Spring Security 5.8, useAuthorizationManager
was added to @EnableReactiveMethodSecurity
to allow applications to opt in to AuthorizationManager
's features.
Change useAuthorizationManager
to true
To opt in, change useAuthorizationManager
to true
like so:
-
Java
-
Kotlin
@EnableReactiveMethodSecurity
@EnableReactiveMethodSecurity
changes to:
-
Java
-
Kotlin
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
Check for AnnotationConfigurationException
s
useAuthorizationManager
activates stricter enforcement of Spring Security’s non-repeatable or otherwise incompatible annotations.
If after turning on useAuthorizationManager
you see AnnotationConfigurationException
s in your logs, follow the instructions in the exception message to clean up your application’s method security annotation usage.
Opt-out Steps
If you ran into trouble with AuthorizationManager
for reactive method security, you can opt out by changing:
-
Java
-
Kotlin
@EnableReactiveMethodSecurity
@EnableReactiveMethodSecurity
to:
-
Java
-
Kotlin
@EnableReactiveMethodSecurity(useAuthorizationManager = false)
@EnableReactiveMethodSecurity(useAuthorizationManager = false)
Propagate AuthenticationServiceException
s
AuthenticationFilter
propagates AuthenticationServiceException
s to the ServerAuthenticationEntryPoint
.
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.
Configure ServerAuthenticationFailureHandler
to rethrow AuthenticationServiceException
s
To prepare for the 6.0 default, httpBasic
and oauth2ResourceServer
should be configured to rethrow AuthenticationServiceException
s.
For each, construct the appropriate authentication entry point for httpBasic
and for oauth2ResourceServer
:
-
Java
-
Kotlin
ServerAuthenticationEntryPoint bearerEntryPoint = new BearerTokenServerAuthenticationEntryPoint();
ServerAuthenticationEntryPoint basicEntryPoint = new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED);
val bearerEntryPoint: ServerAuthenticationEntryPoint = BearerTokenServerAuthenticationEntryPoint()
val basicEntryPoint: ServerAuthenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)
If you use a custom |
Then, construct and configure a ServerAuthenticationEntryPointFailureHandler
for each one:
-
Java
-
Kotlin
AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint);
bearerFailureHandler.setRethrowAuthenticationServiceException(true);
AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint);
basicFailureHandler.setRethrowAuthenticationServiceException(true)
val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint)
bearerFailureHandler.setRethrowAuthenticationServiceException(true)
val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint)
basicFailureHandler.setRethrowAuthenticationServiceException(true)
Finally, wire each authentication failure handler into the DSL, like so:
-
Java
-
Kotlin
http
.httpBasic((basic) -> basic.authenticationFailureHandler(basicFailureHandler))
.oauth2ResourceServer((oauth2) -> oauth2.authenticationFailureHandler(bearerFailureHandler))
http {
httpBasic {
authenticationFailureHandler = basicFailureHandler
}
oauth2ResourceServer {
authenticationFailureHandler = bearerFailureHandler
}
}
Add @Configuration
annotation
In 6.0, @Configuration
is removed from @EnableWebFluxSecurity
and @EnableReactiveMethodSecurity
.
To prepare for this, wherever you are using one of these annotations, you may need to add @Configuration
.
For example, @EnableReactiveMethodSecurity
changes from:
-
Java
@EnableReactiveMethodSecurity
public class MyConfiguration {
// ...
}
-
Kotlin
@EnableReactiveMethodSecurity
open class MyConfiguration {
// ...
}
to:
-
Java
@Configuration
@EnableReactiveMethodSecurity
public class MyConfiguration {
// ...
}
-
Kotlin
@Configuration
@EnableReactiveMethodSecurity
open class MyConfiguration {
// ...
}
Address OAuth2 Client Deprecations
ServerOAuth2AuthorizedClientExchangeFilterFunction
The method setAccessTokenExpiresSkew(…)
can be replaced with one of:
-
ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(…)
-
RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(…)
-
JwtBearerReactiveOAuth2AuthorizedClientProvider#setClockSkew(…)
The method setClientCredentialsTokenResponseClient(…)
can be replaced with the constructor ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)
.
See Client Credentials for more information. |
Add @Configuration
to @Enable*
annotations
In 6.0, all Spring Security’s @Enable*
annotations had their @Configuration
removed.
While convenient, it was not consistent with the rest of the Spring projects and most notably Spring Framework’s @Enable*
annotations.
Additionally, the introduction of support for @Configuration(proxyBeanMethods=false)
in Spring Framework provides another reason to remove @Configuration
meta-annotation from Spring Security’s @Enable*
annotations and allow users to opt into their preferred configuration mode.
The following annotations had their @Configuration
removed:
-
@EnableGlobalAuthentication
-
@EnableGlobalMethodSecurity
-
@EnableMethodSecurity
-
@EnableReactiveMethodSecurity
-
@EnableWebSecurity
-
@EnableWebFluxSecurity
For example, if you are using @EnableWebFluxSecurity
, you will need to change:
-
Java
@EnableWebFluxSecurity
public class SecurityConfig {
// ...
}
to:
-
Java
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
// ...
}
And the same applies to every other annotation listed above.