java 在 SockJS+Spring Websocket 中,convertAndSendToUser 中的“用户”来自哪里?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37853727/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:53:04  来源:igfitidea点击:

Where "user" comes from in convertAndSendToUser works in SockJS+Spring Websocket?

javaspringstompspring-websocketsockjs

提问by Askar Ibragimov

I would like to understand how convertAndSendToUser works in Spring SockJS+Websocket framework.

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

In client, we would connect as

在客户端,我们将连接为

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

which will result in connect request with "Stomp credentials" of login and password, that can be seen e.g. if we handle SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/

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

But it remains unclear to me whether this will be the "user" meant in server-side send operation to a queue:

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

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

The closest I can get is to read this thread Sending message to specific user on Spring Websocket, answer by Thanh Nguyen Van, but it is still unclear.

我能得到的最接近的是阅读这个线程Sending message to specific user on Spring Websocket,由 Thanh Nguyen Van 回答,但仍不清楚。

Basically what I need to do, is to subscribe some clients to same topic, but on server, send them different data. Client may supply user identifier.

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

回答by Siddharth Garg

We know we can send messages to the client from a stomp server using the topic prefixes that he is subscribed to e.g. /topic/hello. We also know we can send messages to a specific user because spring provides the convertAndSendToUser(username, destination, message)API. It accepts a String username which means if we somehow have a unique username for every connection, we should be able to send messages to specific users subscribed to a topic.

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

What's less understood is, where does this username come from ?

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

This username is part of a java.security.Principalinterface.Each StompHeaderAccessoror WebSocketSessionobject has instance of this principal and you can get the user name from it. However, as per my experiments, it is not generated automatically. It has to be generated manually by the server for every session.

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

To use this interface first you need to implement it.

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

class StompPrincipal implements Principal {
    String name

    StompPrincipal(String name) {
        this.name = name
    }

    @Override
    String getName() {
        return name
    }
}

Then you can generate a unique StompPrincipalfor every connection by overriding the DefaultHandshakeHandler. You can use any logic to generate the username. Here is one potential logic which uses UUID :

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

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

Lastly, you need to configure your websockets to use your custom handshake handler.

最后,您需要配置您的 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
}

That's It. Now your server is configured to generate a unique principal name for every connection. It will pass that principal as part of StompHeaderAccessorobjects that you can access through connection event listeners, MessageMapping functions etc...

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

From event listeners :

从事件侦听器:

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

From Message Mapped APIs

来自消息映射 API

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

One last note about using convertAndSendToUser(...). When sending messages to a user, you will use something like this

关于使用convertAndSendToUser(...). 向用户发送消息时,您将使用这样的东西

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

However, for subscribing the client, you will use

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

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

If you subscribe the client to /topic/helloyou will only receive broadcasted messages.

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

回答by Wenneguen

I did not do any specific configuration and I can just do this:

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

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