简体   繁体   English

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

[英]Where “user” comes from in convertAndSendToUser works in SockJS+Spring Websocket?

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 eg 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.客户可以提供用户标识符。

We know we can send messages to the client from a stomp server using the topic prefixes that he is subscribed to eg /topic/hello .我们知道我们可以使用他订阅的主题前缀从 stomp 服务器向客户端发送消息,例如/topic/hello We also know we can send messages to a specific user because spring provides the convertAndSendToUser(username, destination, message) API.我们也知道我们可以向特定用户发送消息,因为 spring 提供了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.它接受一个字符串用户名,这意味着如果我们以某种方式为每个连接都有一个唯一的用户名,我们应该能够向订阅主题的特定用户发送消息。

What's less understood is, where does this username come from ?不太了解的是,这个用户名来自哪里?

This username is part of a java.security.Principal interface.此用户名是java.security.Principal接口的一部分。 Each StompHeaderAccessor or WebSocketSession object has instance of this principal and you can get the user name from it.每个StompHeaderAccessorWebSocketSession对象都有此主体的实例,您可以从中获取用户名。 However, as per my experiments, it is not generated automatically.但是,根据我的实验,它不是自动生成的。 It has to be generated manually by the server for every session.它必须由服务器为每个会话手动生成。

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 StompPrincipal for every connection by overriding the DefaultHandshakeHandler.然后,您可以通过覆盖 DefaultHandshakeHandler 为每个连接生成唯一的StompPrincipal You can use any logic to generate the username.您可以使用任何逻辑来生成用户名。 Here is one potential logic which uses UUID :这是使用 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 StompHeaderAccessor objects that you can access through connection event listeners, MessageMapping functions etc...它将将该主体作为StompHeaderAccessor对象的一部分传递,您可以通过连接事件侦听器、MessageMapping 函数等访问StompHeaderAccessor对象...

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(...) .关于使用convertAndSendToUser(...)最后一个注意事项。 When sending messages to a user, you will use something like this向用户发送消息时,您将使用这样的东西

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/hello you will only receive broadcasted messages.如果您将客户端订阅到/topic/hello您将只会收到广播消息。

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();
}

Similar to Wenneguen I was able to do just by injecting Principal in the MessageMapping method与 Wenneguen 类似,我只需在MessageMapping方法中注入Principal即可完成

public void processMessageFromClient(@Payload String message, Principal principal) {

the principal.getName() implementation is from org.springframework.security.authentication.UsernamePasswordAuthenticationToken class principal.getName()实现来自org.springframework.security.authentication.UsernamePasswordAuthenticationToken

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM