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

Posted

技术标签:

【中文标题】如何使用 Spring WebSocket 向 STOMP 客户端发送错误消息?【英文标题】:How to send ERROR message to STOMP clients with Spring WebSocket? 【发布时间】:2016-02-17 21:55:14 【问题描述】:

我正在使用 Spring 的 STOMP over 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】:

您不能只在clientInboundChannel 上从MySubscriptionInterceptor 抛出异常,因为最后一个是ExecutorSubscribableChannel,因此是async,并且来自这些线程的任何异常最终都会以任何重新- 扔给调用者 - StompSubProtocolHandler.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();
    

【讨论】:

感谢@Artem,我试了一下。不幸的是,当@SubscribeMapping 方法中抛出异常并由@MessageExceptionHandler 方法处理时,一条消息被发送到客户端的错误队列,但他们的订阅仍然转发给代理,客户端仍然从主题。 所以,尝试直接使用sendclientOutboundChannel 从那个@MessageExceptionHandler @ArtemBilan 你能解释一下如何获得 OutboundChannel 吗?提出问题:***.com/questions/39641477/…

以上是关于如何使用 Spring WebSocket 向 STOMP 客户端发送错误消息?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过spring websocket STOMP向特定订阅发送消息?

在 Spring Websocket 上向特定用户发送消息

如何使用 spring+stomp+sockjs 获得 websocket 响应

带有 Spring-boot 后端的 Flutter websocket

如何使用和自定义 MessageConversion(Spring websocket 客户端)

Spring:向 websocket 客户端发送消息