diff --git a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc index 048f9b0b55..03fcb46c60 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc @@ -205,6 +205,78 @@ This will ensure that: <5> Any other message of type MESSAGE or SUBSCRIBE is rejected. Due to 6 we do not need this step, but it illustrates how one can match on specific message types. <6> Any other Message is rejected. This is a good idea to ensure that you do not miss any messages. +[[migrating-spel-expressions]] +=== Migrating SpEL Expressions + +If you are migrating from an older version of Spring Security, your destination matchers may include SpEL expressions. +It's recommended that these be changed to using concrete implementations of `AuthorizationManager` since this is independently testable. + +However, to ease migration, you can also use a class like the following: + +[source,java] +---- +public final class MessageExpressionAuthorizationManager implements AuthorizationManager> { + + private SecurityExpressionHandler> expressionHandler = new DefaultMessageSecurityExpressionHandler(); + + private Expression expression; + + public MessageExpressionAuthorizationManager(String expressionString) { + Assert.hasText(expressionString, "expressionString cannot be empty"); + this.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString); + } + + @Override + public AuthorizationDecision check(Supplier authentication, MessageAuthorizationContext context) { + EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context.getMessage()); + boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx); + return new ExpressionAuthorizationDecision(granted, this.expression); + } + +} +---- + +And specify an instance for each matcher that you cannot get migrate: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +public class WebSocketSecurityConfig { + + @Bean + public AuthorizationManager> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) { + messages + // ... + .simpSubscribeDestMatchers("/topic/friends/{friend}").access(new MessageExpressionAuthorizationManager("#friends == 'john")); + // ... + + return messages.build(); + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Configuration +open class WebSocketSecurityConfig { + fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager { + messages + // .. + .simpSubscribeDestMatchers("/topic/friends/{friends}").access(MessageExpressionAuthorizationManager("#friends == 'john")) + // ... + + return messages.build() + } +} +---- +====== + [[websocket-authorization-notes]] === WebSocket Authorization Notes