哪个“user”来自convertAndSendToUser在SockJS + Spring Websocket中工作?

我想了解convertAndSendToUser如何在Spring SockJS + Websocket框架中工作。

在客户端,我们将连接为

stompClient.connect(login, password, callback()) 

这将导致连接请求与登录和密码的“Stomp凭证”,例如,如果我们处理SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in -spring-4 /

但我仍然不清楚这是否是服务器端向队列发送操作的“用户”:

  simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message); 

我能得到的最接近的是阅读这个post在Spring Websocket上向特定用户发送消息 ,由Thanh Nguyen Van回答,但目前还不清楚。

基本上我需要做的是订阅一些客户端到同一主题,但在服务器上,发送不同的数据。 客户可以提供用户标识符。

我一直试图理解websockets并遇到了这个问题。 没有找到答案,我感到非常失望。 这是未来读者的一些东西。

我将假设读者对使用Stomp的弹簧腹板有基本的了解。 诸如订阅,目的地前缀,主题,套接字配置文件等术语被理解。

我们知道我们可以使用他订阅的主题前缀例如/topic/hello从stomp服务器向客户端发送消息。 我们也知道我们可以向特定用户发送消息,因为spring提供了convertAndSendToUser(username, destination, message) API。 它接受一个String用户名,这意味着如果我们以某种方式为每个连接都有一个唯一的用户名,我们应该能够向订阅主题的特定用户发送消息。

不太了解的是,这个用户名来自哪里?

此用户名是java.security.Principal接口的一部分。 每个StompHeaderAccessorWebSocketSession对象都具有此主体的实例,您可以从中获取用户名。 但是,根据我的实验,它不是自动生成的。 它必须由服务器为每个会话手动生成。

要首先使用此接口,您需要实现它。

 class StompPrincipal implements Principal { String name StompPrincipal(String name) { this.name = name } @Override String getName() { return name } } 

然后,您可以通过覆盖DefaultHandshakeHandler为每个连接生成唯一的StompPrincipal 。 您可以使用任何逻辑来生成用户名。 这是一个使用UUID的潜在逻辑:

 class CustomHandshakeHandler extends DefaultHandshakeHandler { // Custom class for storing principal @Override protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map attributes) { // Generate principal with UUID as name return new StompPrincipal(UUID.randomUUID().toString()) } } 

最后,您需要配置websockets以使用自定义握手处理程序。

 @Override void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) { stompEndpointRegistry .addEndpoint("/stomp") // Set websocket endpoint to connect to .setHandshakeHandler(new CustomHandshakeHandler()) // Set custom handshake handler .withSockJS() // Add Sock JS support } 

而已。 现在,您的服务器配置为为每个连接生成唯一的主体名称。 它会将该主体作为StomHeaderAccessor对象的一部分传递,您可以通过连接事件侦听器,MessageMapping函数等访问它们…

来自事件监听器:

 @EventListener void handleSessionConnectedEvent(SessionConnectedEvent event) { // Get Accessor StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()) } 

来自消息映射的API

 @MessageMapping('/hello') protected void hello(SimpMessageHeaderAccessor sha, Map message) { // sha available in params } 

关于使用convertAndSendToUser(...)最后一点说明。 向用户发送消息时,您将使用类似的内容

 convertAndSendToUser(sha.session.principal.name, '/topic/hello', message) 

但是,对于订阅客户端,您将使用

 client.subscribe('/user/topic/hello', callback) 

如果您将客户端订阅到/topic/hello您将只收到广播的消息。

我没有做任何特定的配置,我可以这样做:

 @MessageMapping('/hello') protected void hello(Principal principal, Map message) { String username = principal.getName(); }