From 31a1486b8834deb84f0466f023eb8d82fff18bff Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Thu, 27 Oct 2022 20:08:13 -0600 Subject: [PATCH] Add Message Security Preparation Steps Issue gh-11337 --- docs/modules/ROOT/pages/migration.adoc | 267 +++++++++++++++++++++++++ 1 file changed, 267 insertions(+) diff --git a/docs/modules/ROOT/pages/migration.adoc b/docs/modules/ROOT/pages/migration.adoc index 8fca4f3d6c2..376d0a62782 100644 --- a/docs/modules/ROOT/pages/migration.adoc +++ b/docs/modules/ROOT/pages/migration.adoc @@ -212,6 +212,273 @@ companion object { `@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