如何使用Spring WebSocket向STOMP客户端发送ERROR消息?

时间:2015-11-16 17:46:40

标签: java activemq stomp spring-websocket spring-messaging

我使用Spring的STOMP而不是WebSocket实现,使用功能齐全的ActiveMQ代理。当用户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,但到目前为止,我无法从订阅异常中获得有关订阅异常的错误消息服务器交付给客户端。这显然不太理想,因为客户从未被告知他们被拒绝访问。

1 个答案:

答案 0 :(得分:7)

您不能仅从MySubscriptionInterceptor上的clientInboundChannel处抛出异常,因为最后一个是ExecutorSubscribableChannel,因此asyncStompSubProtocolHandler.handleMessageFromClient的例外情况线程最终会出现在日志中,并且会向调用者重新投掷 - clientOutboundChannel

但你可以做的就像StompHeaderAccessor headerAccessor = StompHeaderAccessor.create(StompCommand.ERROR); headerAccessor.setMessage(error.getMessage()); clientOutboundChannel.send(MessageBuilder.createMessage(new byte[0], headerAccessor.getMessageHeaders())); 一样,并按照这样使用它:

    @SubscribeMapping("/foo")
    public void handleWithError() {
        throw new IllegalArgumentException("Bad input");
    }

    @MessageExceptionHandler
    @SendToUser("/queue/error")
    public String handleException(IllegalArgumentException ex) {
        return "Got error: " + ex.getMessage();
    }

另一个需要考虑的选择是注释映射:

$(this).width()