使用 Spring WebSocket 的 SimpMessagingTemplate 进行多端点配置

Kri*_*off 5 java spring websocket spring-4 spring-websocket

我使用 Spring 4.3.5 和 WebSocket 以及 SockJS、STOMP 和 SimpleBrokerMessageHandler。

在我的应用程序中,我有三个单独的 WebSocket 端点在不同的地址上运行:/endPointA、/ednpointB、/endpointC 更具体地说,我有三个单独的配置类,并用 @Configuration @EnableWebSocketMessageBroker 注释进行注释。

我还有一个具有 @Autowired SimpMessagingTemplate 的类。

最后,我有三个客户端,每个客户端都连接到一个不同的端点。然而,所有这些都订阅了“相同”的频道地址,即/topic/messages

  • ClientOne 连接端点A
  • ClientTwo 连接端点B
  • ClientThree连接的是端点C

当我使用SimpMessagingTemplate向/topic/messages发送内容时,所有客户端都会收到此消息。

之后我有两个问题:

  1. 有没有办法“隔离”Web Socket 端点,以便消息不会传播到所有端点?
  2. 为什么这实际上发生在这里?

我做了一些调查(堆转储分析),发现对于我的配置,我有:

  • SimpMessagingTemplate的三个实例,但是我始终使用同一个实例来发送消息(因为 @Autowire - 另外我正在打印 SimpMessagingTemplate.toString())。
  • SimpleBrokerMessageHandler的一个实例
  • SockJsWebSocketHandler的三个实例

所以我想知道,在所有端点上的消息传播是否是 SimpleBrokerMessageHandler 或 SimpMessagingTemplate 的“功能”。

asy*_*ind 3

我在多租户应用程序中遇到了同样的问题,我通过这个线程解决了这个问题:

我的 websocket 端点是:ws://127.0.0.1/my-context-app/ws ,java 配置文件是:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

   @Override
   public void registerStompEndpoints(StompEndpointRegistry registry) {       
      registry.addEndpoint("/ws").setAllowedOrigins("*");
      registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); 
   }
}
Run Code Online (Sandbox Code Playgroud)

我的 websocket 队列 url 表单以tenant-id: 为前缀/[tenant-id]/[url-of-queue]

每个客户端都订阅自己的租户 ID。WebSocketSecurityConfig.configureInbound(MessageSecurityMetadataSourceRegistry)由于方法和具有“websocket-queue-subscription-security-check”方法的自定义 spring bean,它无法订阅另一个客户端的队列:

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
          .simpSubscribeDestMatchers("/**")
          .access("@customWSSecurityCheck.isSubscriptionAllowed(authentication, message)");
    }

}
Run Code Online (Sandbox Code Playgroud)

我的自定义 bean 名为 customWSSecurityCheck 检查是否允许经过身份验证的用户订阅队列。请记住,使用附加的tenantId属性CustomAuthentication来实现org.springframework.security.core.Authentication,该属性由自定义Spring安全过滤器/身份验证方法中未提及的附加代码填充:

@Bean()
public class CustomWSSecurityCheck {

    public boolean isSubscriptionAllowed(CustomAuthentication authentication, Message message) {

       StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
       String url = sha.getDestination().substring(1);
       String tenantId = url.substring(0, url.indexOf("/"));

       return tenantId.equals(authentication.getTenantId());
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,这涉及到服务器发送的每条消息都应该以正确的租户 ID 为前缀:MessagingService.convertAndSend("[tenant-id]/[url-of-queue]", messagePayload)