フル機能のActiveMQブローカーでSpringのSTOMPoverWebSocket実装を使用しています。ユーザーがトピックにSUBSCRIBE
する場合、正常にサブスクライブする前に通過する必要のあるいくつかの権限ロジックがあります。以下に構成されているように、ChannelInterceptorを使用してアクセス許可ロジックを適用しています。
WebSocketConfig.Java:
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp")
.setAllowedOrigins("*")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost("relayhost.mydomain.com")
.setRelayPort(61613);
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new MySubscriptionInterceptor());
}
}
WebSocketSecurityConfig.Java:
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpSubscribeDestMatchers("/stomp/**").authenticated()
.simpSubscribeDestMatchers("/user/queue/errors").authenticated()
.anyMessage().denyAll();
}
}
MySubscriptionInterceptor.Java:
public class MySubscriptionInterceptor extends ChannelInterceptorAdapter {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor headerAccessor= StompHeaderAccessor.wrap(message);
Principal principal = headerAccessor.getUser();
if (StompCommand.SUBSCRIBE.equals(headerAccessor.getCommand())) {
checkPermissions(principal);
}
return message;
}
private void checkPermissions(Principal principal) {
// apply permissions logic
// throw Exception permissions not sufficient
}
}
適切な権限を持たないクライアントが制限されたトピックをサブスクライブしようとすると、トピックから実際にメッセージを受信することはありませんが、サブスクリプションを拒否したスローされた例外についても通知されません。代わりに、クライアントには、ActiveMQブローカーが何も知らないデッドサブスクリプションが返されます。 (STOMPエンドポイントおよびトピックとの通常の適切に許可されたクライアントの対話は期待どおりに機能します。)
正常に接続された後、Javaテストクライアントでusers/{subscribingUsername}/queue/errors
とプレーンなusers/queue/errors
をサブスクライブしようとしましたが、これまでのところ、ある種のエラーを取得できませんでしたクライアントに配信されたサーバーからのサブスクリプション例外に関するメッセージ。これは、クライアントがアクセスを拒否されたことを通知されないため、明らかに理想的とは言えません。
MySubscriptionInterceptor
のclientInboundChannel
から例外をスローすることはできません。最後の例外はExecutorSubscribableChannel
であるため、async
とそれらのスレッドからの例外です。呼び出し元への再スローとともにログに記録されます-StompSubProtocolHandler.handleMessageFromClient
。
しかし、そこでできることはclientOutboundChannel
のようなもので、次のように使用します。
StompHeaderAccessor headerAccessor = StompHeaderAccessor.create(StompCommand.ERROR);
headerAccessor.setMessage(error.getMessage());
clientOutboundChannel.send(MessageBuilder.createMessage(new byte[0], headerAccessor.getMessageHeaders()));
考慮すべきもう1つのオプションは、注釈マッピングです。
@SubscribeMapping("/foo")
public void handleWithError() {
throw new IllegalArgumentException("Bad input");
}
@MessageExceptionHandler
@SendToUser("/queue/error")
public String handleException(IllegalArgumentException ex) {
return "Got error: " + ex.getMessage();
}