[[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. ==== 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. ''' [[reactive-change-to-useauthorizationmanager]] ==== Change `useAuthorizationManager` to `true` 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. 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) ---- ==== [NOTE] ===== In 6.0, `useAuthorizationManager` defaults to `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.