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


84

如何仅从服务器向特定用户发送websocket消息?

我的webapp具有spring安全设置,并使用websocket。我在尝试仅从服务器向特定用户发送消息时遇到棘手的问题。

通过阅读手册,我的理解是来自我们可以做的服务器

simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);

在客户端:

stompClient.subscribe('/user/reply', handler);

但是我永远无法调用订阅回调。我尝试了许多不同的方法,但是没有运气。

如果我将其发送到/ topic / reply,它可以工作,但所有其他已连接的用户也将收到它。

为了说明问题,我在github上创建了这个小项目:https : //github.com/gerrytan/wsproblem

重现步骤:

1)克隆并构建项目(确保您使用的是jdk 1.7和maven 3.1)

$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run

2)导航到http://localhost:8080,使用bob / test或jim / test登录

3)单击“请求用户特定的味精”。预期:仅此用户的“仅收到我的消息”旁边会显示一条消息“ hello {username}”,实际:未收到任何消息


您是否一直在看convertAndSendToUser(String user,String destination,T message)吗?docs.spring.io/spring/docs/4.0.0.M3/javadoc-api/org/…–
Viktor K.

是的,也尝试过,但是没有运气
快活的人2014年

我一直在从事私人项目,这是我们使用的方法,它正在为我们工作。我认为一个潜在的问题可能是您订阅了“ / user / reply”,并且正在向“ / user / {username} / reply”发送消息。我认为您应该删除{username}部分,并使用convertAndSendToUser(String user,String destination,T message)。
维克多·K

谢谢,但是我尝试了simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/user/reply", reply);,当消息从服务器发送时,它抛出了这个异常java.lang.IllegalArgumentException: Expected destination pattern "/principal/{userId}/**"
gerrytan 2014年

@ViktorK。是正确的,您已经接近正确的解决方案。您在客户端的订阅是正确的,您只需尝试:convertAndSendToUser(principal.getName(), "/reply", reply);
提示

Answers:


80

哦,client side no need to known about current user服务器会为您做到这一点。

在服务器端,使用以下方式将消息发送给用户:

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

注意:使用queue,而不是topic,Spring始终queue与一起使用sendToUser

在客户端

stompClient.subscribe("/user/queue/reply", handler);

说明

当任何websocket连接打开时,Spring会为其分配一个session id(而不是HttpSession为每个连接分配)。当您的客户订阅以开头的频道/user/(例如:)时/user/queue/reply,您的服务器实例将订阅一个名为queue/reply-user[session id]

使用发送消息给用户时,例如:用户名是admin 您将写simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

Spring将确定哪个session id映射到user admin。例如:它发现了两个会话wsxedc123thnujm456,Spring会将其转换为2个目标queue/reply-userwsxedc123queue/reply-userthnujm456,并将带有2个目标的消息发送到消息代理。

消息代理接收消息并将其提供回您的服务器实例,即与每个会话相对应的保持会话(WebSocket会话可以由一个或多个服务器保持)。Spring会将消息翻译为destination(例如:)user/queue/replysession id(例如:)wsxedc123。然后,它将消息发送到相应的Websocket session


7
您能说明一下用户名吗?在您的示例中,您说用户名是“ admin”,您从用户那里收到了用户名?
Jorj

1
用户名是从HttpSession启动Websocket连接时获得的
Thanh Nguyen Van

1
请您提供更多有关如何设置用户名的信息吗?无论如何,我可以在订阅消息上发送用户名吗?
安德烈斯(Andres)

1
@Andres:您可以扩展DefaultHandshakeHandler和覆盖方法determineUser
Thanh Nguyen Van

1
您的解释效果很好。但是queue/reply-user[session id]正式文件中的哪一部分?
hbrls

35

啊,我发现了我的问题所在。首先,我没有/user在简单代理上注册前缀

<websocket:simple-broker prefix="/topic,/user" />

然后我/user在发送时不需要多余的前缀:

convertAndSendToUser(principal.getName(), "/reply", reply);

Spring将自动添加"/user/" + principal.getName()到目的地,因此解析为“ / user / bob / reply”。

这也意味着在javascript中,我必须为每个用户订阅不同的地址

stompClient.subscribe('/user/' + userName + '/reply,...) 

3
嗯...有什么办法可以避免在客户端设置用户名?通过更改该值(例如,使用其他用户名),您将能够看到其他人的消息。
vdenotaris 2014年


如何从带有注释的方法订阅回复用户@RequestMapping,还@MessageMapping请参见此处如何在特定的userName(userId)上使用Sping Websocket集成进行订阅+从带有@RequestMapping注释的方法获取通知?
Shantaram Tupe

嘿,我的朋友你能告诉我吗?这个“用户”到底是什么?我正在使用Spring Security,我的用户(用户名)是电子邮件地址。每个用户登录时,都会在Spring Security上获取他的JWT令牌。
弗朗西斯科·

我遇到了同样的问题,经过数小时的搜索才找到答案。只有你的爆炸帮助了我。非常感谢!!
Navaneeth,

3

我也使用STOMP创建了一个示例Websocket项目。我注意到的是

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic", "/queue");// including /user also works
    config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/getfeeds").withSockJS();
}

}

无论config.enableSimpleBroker(...


2

我的解决方案基于Thanh Nguyen Van的最佳解释,但除此之外,我还配置了MessageBrokerRegistry:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue/", "/topic/");
        ...
    }
    ...
}

2

确实我做了同样的事情,而且它在不使用用户的情况下也可以正常工作

@Configuration
@EnableWebSocketMessageBroker  
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
       registry.addEndpoint("/gs-guide-websocket").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic" , "/queue");
        config.setApplicationDestinationPrefixes("/app");
    }
}

嘿,您在哪里使用过/app?在这种情况下?
弗朗西斯科·索扎
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.