|
For the latest stable version, please use Spring Security 6.3.3! |
|
For the latest stable version, please use Spring Security 6.3.3! |
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 AnnotationConfigurationExceptions
useAuthorizationManager activates stricter enforcement of Spring Security’s non-repeatable or otherwise incompatible annotations.
If after turning on useAuthorizationManager you see AnnotationConfigurationExceptions 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 AuthenticationServiceExceptions
AuthenticationFilter propagates AuthenticationServiceExceptions to the ServerAuthenticationEntryPoint.
Because AuthenticationServiceExceptions 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 AuthenticationServiceExceptions
To prepare for the 6.0 default, httpBasic and oauth2ResourceServer should be configured to rethrow AuthenticationServiceExceptions.
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
}
}
|
If you use a custom |
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. |
|
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.