spring-security/docs/modules/ROOT/pages/migration.adoc

1792 lines
56 KiB
Plaintext
Raw Normal View History

2022-10-24 19:38:58 -04: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 the steps below to minimize changes when
ifdef::spring-security-version[]
xref:6.0.0@migration.adoc[updating to 6.0]
endif::[]
ifndef::spring-security-version[]
updating to 6.0
endif::[]
.
2022-10-24 19:38:58 -04:00
== Servlet
=== Defer Loading CsrfToken
In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
To opt into the new Spring Security 6 default, the following configuration can be used.
.Defer Loading `CsrfToken`
====
.Java
[source,java,role="primary"]
----
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf");
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
val requestHandler = CsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf")
http {
csrf {
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
p:csrfRequestAttributeName="_csrf"/>
----
====
If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
.Explicit Configure `CsrfToken` with 5.8 Defaults
====
.Java
[source,java,role="primary"]
----
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null);
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
val requestHandler = CsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null)
http {
csrf {
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
<b:property name="csrfRequestAttributeName">
<b:null/>
</b:property>
</b:bean>
----
====
=== CSRF BREACH Protection
If the steps for <<Defer Loading CsrfToken>> work for you, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` using the following configuration:
.`CsrfToken` BREACH Protection
====
.Java
[source,java,role="primary"]
----
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf");
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
val requestHandler = XorCsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf")
http {
csrf {
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"
p:csrfRequestAttributeName="_csrf"/>
----
====
=== Explicit Save 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.
To opt into the new Spring Security 6 default, the following configuration can be used.
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[]
=== Use `AuthorizationManager` for Method Security
2022-10-24 19:38:58 -04:00
xref:servlet/authorization/method-security.adoc[Method Security] has been xref:servlet/authorization/method-security.adoc#jc-enable-method-security[simplified] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
2022-10-28 15:52:02 -04:00
Should you run into trouble with making these changes, note that `@EnableGlobalMethodSecurity`, while deprecated, will not be removed in 6.0, allowing you to opt out by sticking with the old annotation.
[[servlet-replace-globalmethodsecurity-with-methodsecurity]]
==== Replace xref:servlet/authorization/method-security.adoc#jc-enable-global-method-security[global method security] with xref:servlet/authorization/method-security.adoc#jc-enable-method-security[method security]
{security-api-url}org/springframework/security/config/annotation/method/configuration/EnableGlobalMethodSecurity.html[`@EnableGlobalMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] are deprecated in favor of {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.html[`@EnableMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-method-security[`<method-security>`], respectively.
The new annotation and XML element activate Spring's xref:servlet/authorization/method-security.adoc#jc-enable-method-security[pre-post annotations] by default and use `AuthorizationManager` internally.
2022-10-24 19:38:58 -04:00
This means that the following two listings are functionally equivalent:
====
.Java
[source,java,role="primary"]
----
@EnableGlobalMethodSecurity(prePostEnabled = true)
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableGlobalMethodSecurity(prePostEnabled = true)
----
.Xml
[source,xml,role="secondary"]
----
<global-method-security pre-post-enabled="true"/>
----
2022-10-24 19:38:58 -04:00
====
and:
2022-10-24 19:38:58 -04:00
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity
----
.Xml
[source,xml,role="secondary"]
----
<method-security/>
----
2022-10-24 19:38:58 -04:00
====
For applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior.
2022-10-24 19:38:58 -04:00
For example, a listing like:
====
.Java
[source,java,role="primary"]
----
@EnableGlobalMethodSecurity(securedEnabled = true)
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableGlobalMethodSecurity(securedEnabled = true)
----
.Xml
[source,xml,role="secondary"]
----
<global-method-security secured-enabled="true"/>
----
2022-10-24 19:38:58 -04:00
====
should change to:
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)
----
.Xml
[source,xml,role="secondary"]
----
<method-security secured-enabled="true" pre-post-enabled="false"/>
----
2022-10-24 19:38:58 -04:00
====
[[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]]
==== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator`
2022-10-24 19:38:58 -04:00
`@EnableMethodSecurity` does not pick up a `PermissionEvaluator`.
This helps keep its API simple.
2022-10-24 19:38:58 -04:00
If you have a custom {security-api-url}org/springframework/security/access/PermissionEvaluator.html[`PermissionEvaluator`] `@Bean`, please change it from:
====
.Java
[source,java,role="primary"]
----
@Bean
static PermissionEvaluator permissionEvaluator() {
2022-10-24 19:38:58 -04:00
// ... your evaluator
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
companion object {
@Bean
fun permissionEvaluator(): PermissionEvaluator {
// ... your evaluator
}
2022-10-24 19:38:58 -04:00
}
----
====
to:
====
.Java
[source,java,role="primary"]
----
@Bean
static MethodSecurityExpressionHandler expressionHandler() {
var expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
return expressionHandler;
2022-10-24 19:38:58 -04:00
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
companion object {
@Bean
fun expressionHandler(): MethodSecurityExpressionHandler {
val expressionHandler = DefaultMethodSecurityExpressionHandler
expressionHandler.setPermissionEvaluator(myPermissionEvaluator)
return expressionHandler
}
2022-10-24 19:38:58 -04:00
}
----
====
==== Replace any custom method-security ``AccessDecisionManager``s
Your application may have a custom {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] or {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] arrangement.
The preparation strategy will depend on your reason for each arrangement.
Read on to find the best match for your situation.
===== I use `UnanimousBased`
If your application uses {security-api-url}org/springframework/security/access/vote/UnanimousBased.html[`UnanimousBased`] with the default voters, you likely need do nothing since unanimous-based is the default behavior with {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.html[`@EnableMethodSecurity`].
However, if you do discover that you cannot accept the default authorization managers, you can use `AuthorizationManagers.allOf` to compose your own arrangement.
Having done that, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
===== I use `AffirmativeBased`
If your application uses {security-api-url}org/springframework/security/access/vote/AffirmativeBased.html[`AffirmativeBased`], then you can construct an equivalent {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`], like so:
====
.Java
[source,java,role="primary"]
----
AuthorizationManager<MethodInvocation> authorization = AuthorizationManagers.anyOf(
// ... your list of authorization managers
)
----
.Kotlin
[source,kotlin,role="secondary"]
----
val authorization = AuthorizationManagers.anyOf(
// ... your list of authorization managers
)
----
====
Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
===== I use `ConsensusBased`
There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
In that case, please implement a composite {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] that takes the set of delegate ``AuthorizationManager``s into account.
Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
===== I use a custom `AccessDecisionVoter`
You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
By way of example, though, here is what adapting {security-api-url}org/springframework/security/access/SecurityMetadataSource.html[`SecurityMetadataSource`] and {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] for `@PreAuthorize` would look like:
====
.Java
[source,java,role="primary"]
----
public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager<MethodInvocation> {
private final SecurityMetadataSource metadata;
private final AccessDecisionVoter voter;
public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) {
ExpressionBasedAnnotationAttributeFactory attributeFactory =
new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory);
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(expressionHandler);
this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice);
}
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) {
List<ConfigAttribute> attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis()));
int decision = this.voter.vote(authentication.get(), invocation, attributes);
if (decision == ACCESS_GRANTED) {
return new AuthorizationDecision(true);
}
if (decision == ACCESS_DENIED) {
return new AuthorizationDecision(false);
}
return null; // abstain
}
}
----
====
Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
===== I use a custom `AfterInvocationManager`
{security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] replaces both {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] and {security-api-url}org/springframework/security/access/intercept/AfterInvocationManager.html[`AfterInvocationManager`].
The difference is that `AuthorizationManager<MethodInvocation>` replaces `AccessDecisionManager` and `AuthorizationManager<MethodInvocationResult>` replaces `AfterInvocationManager`.
Given that, <<_i_use_a_custom_accessdecisionvoter,the same rules apply for adaptation>>, where the goal this time is to implement `AuthorizationManager<MethodInvocationResult>` instead of `AuthorizationManager<MethodInvocation>` and use `AuthorizationManagerAfterMethodInterceptor` instead of `AuthorizationManagerBeforeMethodInterceptor`.
===== I use `RunAsManager`
There is currently https://github.com/spring-projects/spring-security/issues/11331[no replacement for `RunAsManager`] though one is being considered.
It is quite straightforward to adapt a `RunAsManager`, though, to the `AuthorizationManager` API, if needed.
Here is some pseudocode to get you started:
====
.Java
[source,java,role="primary"]
----
public final class RunAsAuthorizationManagerAdapter<T> implements AuthorizationManager<T> {
private final RunAsManager runAs = new RunAsManagerImpl();
private final SecurityMetadataSource metadata;
private final AuthorizationManager<T> authorization;
// ... constructor
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
Supplier<Authentication> wrapped = (auth) -> {
List<ConfigAttribute> attributes = this.metadata.getAttributes(object);
return this.runAs.buildRunAs(auth, object, attributes);
};
return this.authorization.check(wrapped, object);
}
}
----
====
Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`].
[[servlet-check-for-annotationconfigurationexceptions]]
==== Check for ``AnnotationConfigurationException``s
`@EnableMethodSecurity` and `<method-security>` activate stricter enforcement of Spring Security's non-repeatable or otherwise incompatible annotations.
If after moving to either you see ``AnnotationConfigurationException``s in your logs, follow the instructions in the exception message to clean up your application's method security annotation usage.
=== Use `AuthorizationManager` for Message Security
xref:servlet/integrations/websocket.adoc[Message Security] has been xref:servlet/integrations/websocket.adoc#websocket-configuration[improved] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
2022-10-28 15:52:02 -04:00
Should you run into trouble with making these changes, you can follow the <<servlet-authorizationmanager-messages-opt-out,opt out steps>> at the end of this section.
==== Ensure all messages have defined authorization rules
The now-deprecated {security-api-url}org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurer.html[message security support] permits all messages by default.
xref:servlet/integrations/websocket.adoc[The new support] has the stronger default of denying all messages.
To prepare for this, ensure that authorization rules exist are declared for every request.
For example, an application configuration like:
====
.Java
[source,java,role="primary"]
----
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN");
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
}
----
.Xml
[source,xml,role="secondary"]
----
<websocket-message-broker>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
----
====
should change to:
====
.Java
[source,java,role="primary"]
----
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll()
}
----
.Xml
[source,xml,role="secondary"]
----
<websocket-message-broker>
<intercept-message type="CONNECT" access="permitAll"/>
<intercept-message type="DISCONNECT" access="permitAll"/>
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>
----
====
==== Add `@EnableWebSocketSecurity`
[NOTE]
====
If you want to have CSRF disabled and you are using Java configuration, the migration steps are slightly different.
Instead of using `@EnableWebSocketSecurity`, you will override the appropriate methods in `WebSocketMessageBrokerConfigurer` yourself.
Please see xref:servlet/integrations/websocket.adoc#websocket-sameorigin-disable[the reference manual] for details about this step.
====
If you are using Java Configuration, add {security-api-url}org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.html[`@EnableWebSocketSecurity`] to your application.
For example, you can add it to your websocket security configuration class, like so:
====
.Java
[source,java,role="primary"]
----
@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
// ...
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
// ...
}
----
====
This will make a prototype instance of `MessageMatcherDelegatingAuthorizationManager.Builder` available to encourage configuration by composition instead of extension.
==== Use an `AuthorizationManager<Message<?>>` instance
To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager<Message<?>>` `@Bean` in Java.
For example, the following application configuration:
====
.Java
[source,java,role="primary"]
----
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll()
}
----
.Xml
[source,xml,role="secondary"]
----
<websocket-message-broker>
<intercept-message type="CONNECT" access="permitAll"/>
<intercept-message type="DISCONNECT" access="permitAll"/>
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>
----
====
changes to:
====
.Java
[source,java,role="primary"]
----
@Bean
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll();
return messages.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
messages
.simpTypeMatchers(CONNECT, DISCONNECT, UNSUBSCRIBE).permitAll()
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
.anyMessage().denyAll()
return messages.build()
}
----
.Xml
[source,xml,role="secondary"]
----
<websocket-message-broker use-authorization-manager="true">
<intercept-message type="CONNECT" access="permitAll"/>
<intercept-message type="DISCONNECT" access="permitAll"/>
<intercept-message type="UNSUBSCRIBE" access="permitAll"/>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<intercept-message pattern="/**" access="denyAll"/>
</websocket-message-broker>
----
====
==== Stop Implementing `AbstractSecurityWebSocketMessageBrokerConfigurer`
If you are using Java configuration, you can now simply extend `WebSocketMessageBrokerConfigurer`.
For example, if your class that extends `AbstractSecurityWebSocketMessageBrokerConfigurer` is called `WebSocketSecurityConfig`, then:
====
.Java
[source,java,role="primary"]
----
@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
// ...
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: AbstractSecurityWebSocketMessageBrokerConfigurer() {
// ...
}
----
====
changes to:
====
.Java
[source,java,role="primary"]
----
@EnableWebSocketSecurity
@Configuration
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
// ...
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableWebSocketSecurity
@Configuration
class WebSocketSecurityConfig: WebSocketMessageBrokerConfigurer {
// ...
}
----
====
2022-10-28 15:52:02 -04:00
[[servlet-authorizationmanager-messages-opt-out]]
==== Opt-out Steps
2022-10-28 15:52:02 -04:00
In case you had trouble, take a look at these scenarios for optimal opt out behavior:
2022-10-28 15:52:02 -04:00
===== I cannot declare an authorization rule for all requests
2022-10-28 15:52:02 -04:00
If you are having trouble setting an `anyRequest` authorization rule of `denyAll`, please use {security-api-url}org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.Builder.Constraint.html#permitAll()[`permitAll`] instead, like so:
====
.Java
[source,java,role="primary"]
----
2022-10-28 15:52:02 -04:00
@Bean
AuthorizationManager<Message<?>> messageSecurity(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
// ...
2022-10-28 15:52:02 -04:00
.anyMessage().permitAll();
return messages.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
2022-10-28 15:52:02 -04:00
@Bean
fun messageSecurity(val messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<?>> {
messages
.simpDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/admin/**").hasRole("ADMIN")
// ...
2022-10-28 15:52:02 -04:00
.anyMessage().permitAll();
return messages.build()
}
----
.Xml
[source,xml,role="secondary"]
----
2022-10-28 15:52:02 -04:00
<websocket-message-broker use-authorization-manager="true">
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
<!-- ... -->
2022-10-28 15:52:02 -04:00
<intercept-message pattern="/**" access="permitAll"/>
</websocket-message-broker>
----
====
2022-10-28 15:52:02 -04:00
===== I cannot get CSRF working, need some other `AbstractSecurityWebSocketMessageBrokerConfigurer` feature, or am having trouble with `AuthorizationManager`
2022-10-28 15:52:02 -04:00
In the case of Java, you may continue using `AbstractMessageSecurityWebSocketMessageBrokerConfigurer`.
Even though it is deprecated, it will not be removed in 6.0.
2022-10-28 15:52:02 -04:00
In the case of XML, you can opt out of `AuthorizationManager` by setting `use-authorization-manager="false"`:
2022-10-28 15:52:02 -04:00
====
.Xml
[source,xml,role="secondary"]
----
2022-10-28 15:52:02 -04:00
<websocket-message-broker>
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
----
====
2022-10-28 15:52:02 -04:00
to:
====
2022-10-28 15:52:02 -04:00
.Xml
[source,xml,role="secondary"]
----
2022-10-28 15:52:02 -04:00
<websocket-message-broker use-authorization-manager="false">
<intercept-message pattern="/user/queue/errors" access="permitAll"/>
<intercept-message pattern="/admin/**" access="hasRole('ADMIN')"/>
</websocket-message-broker>
----
====
2022-10-28 15:52:02 -04:00
=== Use `AuthorizationManager` for Request Security
2022-10-28 15:52:02 -04:00
xref:servlet/authorization/authorize-requests.adoc[HTTP Request Security] has been xref:servlet/authorization/authorize-http-requests.adoc[simplified] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API].
2022-10-28 15:52:02 -04:00
Should you run into trouble with making these changes, you can follow the <<servlet-authorizationmanager-requests-opt-out,opt out steps>> at the end of this section.
==== Ensure that all requests have defined authorization rules
In Spring Security 5.8 and earlier, requests with no authorization rule are permitted by default.
It is a stronger security position to deny by default, thus requiring that authorization rules be clearly defined for every endpoint.
As such, in 6.0, Spring Security by default denies any request that is missing an authorization rule.
The simplest way to prepare for this change is to introduce an appropriate {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#anyRequest()[`anyRequest`] rule as the last authorization rule.
2022-10-28 15:52:02 -04:00
The recommendation is {security-api-url}org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.AuthorizedUrl.html#denyAll()[`denyAll`] since that is the implied 6.0 default.
[NOTE]
====
You may already have an `anyRequest` rule defined that you are happy with in which case this step can be skipped.
====
Adding `denyAll` to the end looks like changing:
====
.Java
[source,java,role="primary"]
----
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/app/**", hasRole("APP"))
// ...
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http once-per-request="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
----
====
to:
====
.Java
[source,java,role="primary"]
----
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http once-per-request="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
----
====
If you have already migrated to `authorizeHttpRequests`, the recommended change is the same.
==== Switch to `AuthorizationManager`
To opt in to using `AuthorizationManager`, you can use `authorizeHttpRequests` or xref:servlet/appendix/namespace/http.adoc#nsa-http-use-authorization-manager[`use-authorization-manager`] for Java or XML, respectively.
Change:
====
.Java
[source,java,role="primary"]
----
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http once-per-request="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
----
====
to:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
----
====
==== Migrate SpEL expressions to `AuthorizationManager`
For authorization rules, Java tends to be easier to test and maintain than SpEL.
As such, `authorizeHttpRequests` does not have a method for declaring a `String` SpEL.
Instead, you can implement your own `AuthorizationManager` implementation or use `WebExpressionAuthorizationManager`.
For completeness, both options will be demonstrated.
First, if you have the following SpEL:
====
.Java
[source,java,role="primary"]
----
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/complicated/**").access("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/complicated/**", access("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
// ...
authorize(anyRequest, denyAll)
}
}
----
====
Then you can compose your own `AuthorizationManager` with Spring Security authorization primitives like so:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.mvcMatchers("/complicated/**").access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize("/complicated/**", access(anyOf(hasRole("ADMIN"), hasAuthority("SCOPE_read"))
// ...
authorize(anyRequest, denyAll)
}
}
----
====
Or you can use `WebExpressionAuthorizationManager` in the following way:
====
.Java
[source,java,role="primary"]
----
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/complicated/**").access(
new WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')")
)
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/complicated/**", access(
WebExpressionAuthorizationManager("hasRole('ADMIN') || hasAuthority('SCOPE_read')"))
)
// ...
authorize(anyRequest, denyAll)
}
}
----
====
==== Switch to filter all dispatcher types
2022-10-28 15:52:02 -04:00
Spring Security 5.8 and earlier only xref:servlet/authorization/architecture.adoc[perform authorization] once per request.
This means that dispatcher types like `FORWARD` and `INCLUDE` that run after `REQUEST` are not secured by default.
It's recommended that Spring Security secure all dispatch types.
As such, in 6.0, Spring Security changes this default.
So, finally, change your authorization rules to filter all dispatcher types.
To do this, change:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(false)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = false
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
----
====
to:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = true
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
----
====
==== Replace any custom filter-security ``AccessDecisionManager``s
Your application may have a custom {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] or {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] arrangement.
The preparation strategy will depend on your reason for each arrangement.
Read on to find the best match for your situation.
===== I use `UnanimousBased`
If your application uses {security-api-url}org/springframework/security/access/vote/UnanimousBased.html[`UnanimousBased`], you should first adapt or replace any ``AccessDecisionVoter``s and then you can construct an `AuthorizationManager` like so:
====
.Java
[source,java,role="primary"]
----
@Bean
AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
PolicyAuthorizationManager policy = ...;
LocalAuthorizationManager local = ...;
return AuthorizationMangers.allOf(policy, local);
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
val policy: PolicyAuthorizationManager = ...
val local: LocalAuthorizationManager = ...
return AuthorizationMangers.allOf(policy, local)
}
----
.Xml
[source,xml,role="secondary"]
----
<bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
factory-method="allOf">
<constructor-arg>
<util:list>
<bean class="my.PolicyAuthorizationManager"/>
<bean class="my.LocalAuthorizationManager"/>
</util:list>
</constructor-arg>
</bean>
----
====
then, wire it into the DSL like so:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
authorize(anyRequest, requestAuthorization)
}
// ...
}
----
.Xml
[source,xml,role="secondary"]
----
<http authorization-manager-ref="requestAuthorization"/>
----
====
[NOTE]
====
`authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
====
===== I use `AffirmativeBased`
If your application uses {security-api-url}org/springframework/security/access/vote/AffirmativeBased.html[`AffirmativeBased`], then you can construct an equivalent {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`], like so:
====
.Java
[source,java,role="primary"]
----
@Bean
AuthorizationManager<RequestAuthorizationContext> requestAuthorization() {
PolicyAuthorizationManager policy = ...;
LocalAuthorizationManager local = ...;
return AuthorizationMangers.anyOf(policy, local);
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun requestAuthorization(): AuthorizationManager<RequestAuthorizationContext> {
val policy: PolicyAuthorizationManager = ...
val local: LocalAuthorizationManager = ...
return AuthorizationMangers.anyOf(policy, local)
}
----
.Xml
[source,xml,role="secondary"]
----
<bean id="requestAuthorization" class="org.springframework.security.authorization.AuthorizationManagers"
factory-method="anyOf">
<constructor-arg>
<util:list>
<bean class="my.PolicyAuthorizationManager"/>
<bean class="my.LocalAuthorizationManager"/>
</util:list>
</constructor-arg>
</bean>
----
====
then, wire it into the DSL like so:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization))
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
authorize(anyRequest, requestAuthorization)
}
// ...
}
----
.Xml
[source,xml,role="secondary"]
----
<http authorization-manager-ref="requestAuthorization"/>
----
====
[NOTE]
====
`authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern.
See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details.
====
===== I use `ConsensusBased`
There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`].
In that case, please implement a composite {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] that takes the set of delegate ``AuthorizationManager``s into account.
Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[adding a custom `AuthorizationManager`].
===== I use a custom `AccessDecisionVoter`
You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter.
Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution.
By way of example, though, here is what adapting {security-api-url}org/springframework/security/access/SecurityMetadataSource.html[`SecurityMetadataSource`] and {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] for `anyRequest().authenticated()` would look like:
====
.Java
[source,java,role="primary"]
----
public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager<RequestAuthorizationContext> {
private final SecurityMetadataSource metadata;
private final AccessDecisionVoter voter;
public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) {
Map<RequestMatcher, List<ConfigAttribute>> requestMap = Collections.singletonMap(
AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated")));
this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
WebExpressionVoter voter = new WebExpressionVoter();
voter.setExpressionHandler(expressionHandler);
this.voter = voter;
}
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
List<ConfigAttribute> attributes = this.metadata.getAttributes(context);
int decision = this.voter.vote(authentication.get(), invocation, attributes);
if (decision == ACCESS_GRANTED) {
return new AuthorizationDecision(true);
}
if (decision == ACCESS_DENIED) {
return new AuthorizationDecision(false);
}
return null; // abstain
}
}
----
====
Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[adding a custom `AuthorizationManager`].
2022-10-28 15:52:02 -04:00
[[servlet-authorizationmanager-requests-opt-out]]
==== Opt-out Steps
2022-10-24 19:38:58 -04:00
2022-10-28 15:52:02 -04:00
In case you had trouble, take a look at these scenarios for optimal opt out behavior:
2022-10-24 19:38:58 -04:00
2022-10-28 15:52:02 -04:00
===== I cannot secure all dispatcher types
2022-10-24 19:38:58 -04:00
2022-10-28 15:52:02 -04:00
If you cannot secure all dispatcher types, first try and declare which dispatcher types should not require authorization like so:
2022-10-28 15:52:02 -04:00
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.shouldFilterAllDispatcherTypes(true)
.dispatcherTypeMatchers(FORWARD, INCLUDE).permitAll()
.mvcMatchers("/app/**").hasRole("APP")
// ...
.anyRequest().denyAll()
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
shouldFilterAllDispatcherTypes = true
authorize(DispatcherTypeRequestMatcher(FORWARD, INCLUDE), permitAll)
authorize("/app/**", hasRole("APP"))
// ...
authorize(anyRequest, denyAll)
}
}
----
2022-10-28 15:52:02 -04:00
.Xml
[source,xml,role="secondary"]
----
<http filter-all-dispatcher-types="true" use-authorization-manager="true">
<intercept-url request-matcher-ref="dispatchers"/>
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="denyAll"/>
</http>
<bean id="dispatchers" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
<constructor-arg>
<util:list value-type="javax.servlet.DispatcherType">
<value>FORWARD</value>
<value>INCLUDE</value>
</util:list>
</constructor-arg>
</bean>
----
====
2022-10-28 15:52:02 -04:00
Or, if that doesn't work, then you can explicitly opt out of the behavior by setting `filter-all-dispatcher-types` and `filterAllDispatcherTypes` to `false`:
====
.Java
[source,java,role="primary"]
----
2022-10-28 15:52:02 -04:00
http
.authorizeHttpRequests((authorize) -> authorize
.filterAllDispatcherTypes(false)
.mvcMatchers("/app/**").hasRole("APP")
// ...
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
2022-10-28 15:52:02 -04:00
http {
authorizeHttpRequests {
filterAllDispatcherTypes = false
authorize("/messages/**", hasRole("APP"))
// ...
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http filter-all-dispatcher-types="false" use-authorization-manager="true">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
----
====
2022-10-28 15:52:02 -04:00
or, if you are still using `authorizeRequests` or `use-authorization-manager="false"`, set `oncePerRequest` to `true`:
====
.Java
[source,java,role="primary"]
----
2022-10-28 15:52:02 -04:00
http
.authorizeRequests((authorize) -> authorize
.filterSecurityInterceptorOncePerRequest(true)
.mvcMatchers("/app/**").hasRole("APP")
// ...
)
// ...
----
.Kotlin
[source,kotlin,role="secondary"]
----
2022-10-28 15:52:02 -04:00
http {
authorizeRequests {
filterSecurityInterceptorOncePerRequest = true
authorize("/messages/**", hasRole("APP"))
// ...
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http once-per-request="true" use-authorization-manager="false">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
----
====
===== I cannot declare an authorization rule for all requests
If you are having trouble setting an `anyRequest` authorization rule of `denyAll`, please use {security-api-url}org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.AuthorizedUrl.html#permitAll()[`permitAll`] instead, like so:
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpReqeusts((authorize) -> authorize
.mvcMatchers("/app/*").hasRole("APP")
// ...
.anyRequest().permitAll()
)
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
authorize("/app*", hasRole("APP"))
// ...
authorize(anyRequest, permitAll)
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http>
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
<intercept-url pattern="/**" access="permitAll"/>
</http>
----
====
===== I cannot migrate my SpEL or my `AccessDecisionManager`
If you are having trouble with SpEL, `AccessDecisionManager`, or there is some other feature that you are needing to keep using in `<http>` or `authorizeRequests`, try the following.
First, if you still need `authorizeRequests`, you are welcome to keep using it. Even though it is deprecated, it is not removed in 6.0.
Second, if you still need your custom `access-decision-manager-ref` or have some other reason to opt out of `AuthorizationManager`, do:
====
.Xml
[source,xml,role="secondary"]
----
<http use-authorization-manager="false">
<intercept-url pattern="/app/*" access="hasRole('APP')"/>
<!-- ... -->
</http>
----
====
=== 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.
==== Configure `AuthenticationFailureHandler` to rethrow ``AuthenticationServiceException``s
To prepare for the 6.0 default, wire `AuthenticationFilter` instances with a `AuthenticationFailureHandler` that rethrows ``AuthenticationServiceException``s, 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>
----
====
[[servlet-authenticationfailurehandler-opt-out]]
==== Opt-out Steps
If rethrowing ``AuthenticationServiceException``s gives you trouble, you can set the value to false instead of taking the 6.0 default, like so:
====
.Java
[source,java,role="primary"]
----
AuthenticationFilter authenticationFilter = new AuthenticationFilter(...);
AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...);
handler.setRethrowAuthenticationServiceException(false);
authenticationFilter.setAuthenticationFailureHandler(handler);
----
.Kotlin
[source,kotlin,role="secondary"]
----
val authenticationFilter: AuthenticationFilter = new AuthenticationFilter(...)
val handler: AuthenticationEntryPointFailureHandler = new AuthenticationEntryPointFailureHandler(...)
handler.setRethrowAuthenticationServiceException(false)
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="false"/>
</bean>
----
====
2022-10-28 15:52:02 -04:00
== Reactive
=== Use `AuthorizationManager` for Method Security
xref:reactive/authorization/method.adoc[Method Security] has been xref:reactive/authorization/method.adoc#jc-enable-reactive-method-security-authorization-manager[improved] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.
Should you run into trouble with making these changes, you can follow the
<<reactive-authorizationmanager-methods-opt-out,opt out steps>> at the end of this section.
In Spring Security 5.8, `useAuthorizationManager` was added to {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurity.html[`@EnableReactiveMethodSecurity`] to allow applications to opt in to ``AuthorizationManager``'s features.
[[reactive-change-to-useauthorizationmanager]]
==== Change `useAuthorizationManager` to `true`
2022-10-24 19:38:58 -04:00
To opt in, change `useAuthorizationManager` to `true` like so:
====
.Java
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableReactiveMethodSecurity
----
====
changes to:
====
.Java
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
----
====
[[reactive-check-for-annotationconfigurationexceptions]]
==== Check for ``AnnotationConfigurationException``s
2022-10-24 19:38:58 -04:00
`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.
2022-10-28 15:52:02 -04:00
[[reactive-authorizationmanager-methods-opt-out]]
==== Opt-out Steps
If you ran into trouble with `AuthorizationManager` for reactive method security, you can opt out by changing:
====
.Java
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableReactiveMethodSecurity
----
====
to:
====
.Java
[source,java,role="primary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager = false)
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableReactiveMethodSecurity(useAuthorizationManager = false)
----
====