web-dev-qa-db-ja.com

Spring WebSocketを使用してSTOMPクライアントにエラーメッセージを送信するにはどうすればよいですか?

フル機能の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をサブスクライブしようとしましたが、これまでのところ、ある種のエラーを取得できませんでしたクライアントに配信されたサーバーからのサブスクリプション例外に関するメッセージ。これは、クライアントがアクセスを拒否されたことを通知されないため、明らかに理想的とは言えません。

14
hartz89

MySubscriptionInterceptorclientInboundChannelから例外をスローすることはできません。最後の例外は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();
    }
8
Artem Bilan