概述
在这个教程中,我们将介绍如何使用Spring WebSockets向单个用户发送STOMP消息。这很重要,因为我们有时不想将每条消息广播给每个用户。此外,我们还将展示如何以安全的方式发送这些消息。
对于WebSockets的基础知识,请参考这篇入门教程,了解如何快速上手。对于更深入的安全性探讨,可以阅读这篇文章,以保护你的WebSockets实现。
2. 队列、主题和端点
在Spring WebSockets和STOMP中,有三种主要方式来指定消息发送的目的地以及订阅方式:
- 主题(Topics) - 公开的对话或聊天主题,对任何客户端或用户开放。
- 队列(Queues) - 专为特定用户及其当前会话预留。
- 端点(Endpoints) - 通用端点。
现在,让我们快速了解一下每个示例的上下文路径:
-
/topic/movies
-
/user/queue/specific-user
-
/secured/chat
值得注意的是,为了向特定用户发送消息,我们必须使用队列,因为主题和端点不支持这种功能。
3. 配置
现在,让我们学习如何配置应用程序,以便向特定用户发送消息:
public class SocketBrokerConfig extends
WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/secured/history","/secured/user/queue/specific-user");
config.setApplicationDestinationPrefixes("/spring-security-mvc-socket");
config.setUserDestinationPrefix("/secured/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/secured/room").withSockJS();
}
}
确保包含用户目的地非常重要,因为它决定了哪些端点是为单个用户保留的。
我们还将在所有队列和用户目的地前加上/secured
前缀,以要求身份验证。对于不受保护的端点,我们可以去掉/secured
前缀(由于我们的其他安全设置)。
从pom.xml
的角度来看,不需要额外的依赖。
4. URL 映射
我们希望客户端使用以下模式的URL映射来订阅队列:
"/user/queue/updates"
这个映射将由UserDestinationMessageHandler
自动转换为我们已经在客户端订阅的用户会话特定地址。
例如,如果我们有一个名为"user123"
的用户,对应的地址将是:
"/queue/updates-user123"
在服务器端,我们将使用以下URL映射模式发送用户特定响应:
"/user/{username}/queue/updates"
这也会被转换为我们已经在客户端订阅的正确URL映射。
因此,关键在于两点:
- 在指定的用户目的地前缀(在
AbstractWebSocketMessageBrokerConfigurer
中配置)。 - URL映射中包含
/queue
。
在下一节中,我们将详细介绍如何做到这一点。
5. 调用convertAndSendToUser()
我们可以从SimpMessagingTemplate
或SimpMessageSendingOperations
非静态地调用convertAndSendToUser()
:
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
@MessageMapping("/secured/room")
public void sendSpecific(
@Payload Message msg,
Principal user,
@Header("simpSessionId") String sessionId) throws Exception {
OutputMessage out = new OutputMessage(
msg.getFrom(),
msg.getText(),
new SimpleDateFormat("HH:mm").format(new Date()));
simpMessagingTemplate.convertAndSendToUser(
msg.getTo(), "/secured/user/queue/specific-user", out);
}
你可能已经注意到:
@Header("simpSessionId") String sessionId
@Header
注解允许访问由入站消息暴露的头信息。例如,我们可以无需复杂的拦截器直接获取当前的sessionId
。同样,我们可以通过Principal
访问当前用户。
重要的是,本文所采用的方法相对于@sendToUser
注解提供了更大的URL映射定制能力。有关更多关于该注解的信息,请参阅这篇优秀文章。
在客户端,我们将使用JavaScript的connect()
方法初始化一个SockJS实例,并使用STOMP连接到我们的WebSocket服务器:
var socket = new SockJS('/secured/room');
var stompClient = Stomp.over(socket);
var sessionId = "";
stompClient.connect({}, function (frame) {
var url = stompClient.ws._transport.url;
url = url.replace(
"ws://localhost:8080/spring-security-mvc-socket/secured/room/", "");
url = url.replace("/websocket", "");
url = url.replace(/^[0-9]+\//, "");
console.log("Your current session is: " + url);
sessionId = url;
}
我们还会访问提供的sessionId
并将它附加到"secured/room"
的URL映射中。这使我们能够动态且手动提供用户特定的订阅队列:
stompClient.subscribe('secured/user/queue/specific-user'
+ '-user' + that.sessionId, function (msgOut) {
//handle messages
}
设置完成后,我们应该看到:
而在服务器控制台中:
6. 总结
有关此主题的更多信息,请查看Spring官方博客和官方文档。
一如既往,本文中的代码示例可在GitHub上找到。