[[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::[] . == Servlet === 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 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. ''' [[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[``] 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[``], 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. 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"] ---- ---- ==== and: ==== .Java [source,java,role="primary"] ---- @EnableMethodSecurity ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableMethodSecurity ---- .Xml [source,xml,role="secondary"] ---- ---- ==== For applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior. 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"] ---- ---- ==== 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"] ---- ---- ==== ''' [[servlet-replace-permissionevaluator-bean-with-methodsecurityexpression-handler]] ==== Publish a `MethodSecurityExpressionHandler` instead of a `PermissionEvaluator` `@EnableMethodSecurity` does not pick up a `PermissionEvaluator`. This helps keep its API simple. 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() { // ... your evaluator } ---- .Kotlin [source,kotlin,role="secondary"] ---- companion object { @Bean fun permissionEvaluator(): PermissionEvaluator { // ... your evaluator } } ---- ==== to: ==== .Java [source,java,role="primary"] ---- @Bean static MethodSecurityExpressionHandler expressionHandler() { var expressionHandler = new DefaultMethodSecurityExpressionHandler(); expressionHandler.setPermissionEvaluator(myPermissionEvaluator); return expressionHandler; } ---- .Kotlin [source,kotlin,role="secondary"] ---- companion object { @Bean fun expressionHandler(): MethodSecurityExpressionHandler { val expressionHandler = DefaultMethodSecurityExpressionHandler expressionHandler.setPermissionEvaluator(myPermissionEvaluator) return expressionHandler } } ---- ==== ''' [[servlet-check-for-annotationconfigurationexceptions]] ==== Check for ``AnnotationConfigurationException``s `@EnableMethodSecurity` and `` 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. ==== Declare the 5.8 default In case you run into trouble with the ensuing steps and cannot use `AuthorizationManager` at this time, it's recommended as a first step to declare you are using the 5.8 default so that 5.8 behavior is preserved when you update. The only default to change for Method Security is if you are using `` in which case you will change: ==== .Xml [source,xml,role="secondary"] ---- ---- ==== to: ==== .Xml [source,xml,role="secondary"] ---- ---- ==== Later steps will turn this value back on, but now your code is minimally ready for upgrading in case you run into trouble with the remaining steps. ==== 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"] ---- ---- ==== 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"] ---- ---- ==== ==== 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>` instance To start using `AuthorizationManager`, you can set the `use-authorization-manager` attribute in XML or you can publish an `AuthorizationManager>` `@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"] ---- ---- ==== changes to: ==== .Java [source,java,role="primary"] ---- @Bean AuthorizationManager> 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> { 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"] ---- ---- ==== ==== 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 { // ... } ---- ==== == 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. ''' 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. ==== Declare the 5.8 default First, declare the 5.8 default: ==== .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) ---- ==== This is helpful because, if the remaining preparation steps cannot be taken, you can still upgrade to 6.0 while keeping this feature as-is. [[reactive-change-to-useauthorizationmanager]] ==== Change `useAuthorizationManager` to `true` 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 `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.