|
@@ -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<MessageAuthorizationContext<?>> {
|
|
|
+
|
|
|
+ private SecurityExpressionHandler<Message<?>> 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> 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<Message<?>> 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<Message<?> {
|
|
|
+ messages
|
|
|
+ // ..
|
|
|
+ .simpSubscribeDestMatchers("/topic/friends/{friends}").access(MessageExpressionAuthorizationManager("#friends == 'john"))
|
|
|
+ // ...
|
|
|
+
|
|
|
+ return messages.build()
|
|
|
+ }
|
|
|
+}
|
|
|
+----
|
|
|
+======
|
|
|
+
|
|
|
[[websocket-authorization-notes]]
|
|
|
=== WebSocket Authorization Notes
|
|
|
|