Spring Boot实现带STOMP的WebSocket
2021-03-30 21:25
标签:set 标识符 依赖 优秀 start 多个 定义 stomp 缺点 点击左上角,关注:“锅外的大佬” 专注分享国外最新技术内容 服务器端将完全用Java编码。但是,就客户端而言,我将展示用 Java和 JavaScript(SockJS)编写的片段,因为通常, WebSocket客户端嵌入在前端应用程序中。代码示例将演示如何使用 pub-sub模型向多个用户广播消息以及如何仅向单个用户发送消息。在本文的另一部分中,我将简要讨论WebSocket安全问题以及如何确保即使环境不支持 WebSocket协议,基于 WebSocket的解决方案也能运行。 注意, WebSocket安全话题仅在此处简要介绍,因为这是一个非常复杂的问题,可以单独撰写一篇文章。由于这个原因,以及我在文章最后一节 WebSocketinproduction?中提及的因素,我建议在生产中先对安全设置进行修改,直到生产就绪,安全措施到位为止。 WebSocket协议允许应用程序之间实现双向通信。重要的是要知道 HTTP仅用于初始握手。初次握手之后, HTTP连接将升级为被 WebSocket使用的新 TCP/IP连接。 WebSocket协议是一种相当低级的协议。它定义了如何将字节流转换为帧。帧可以包含文本或二进制消息。由于消息本身不提供有关如何路由或处理它的任何其他信息,因此很难在不编写其他代码的情况下实现更复杂的应用程序。幸运的是, WebSocket规范允许在更高的应用程序级别上使用子协议。 STOMP是其中之一,由 SpringFramework支持。 STOMP是一种简单的基于文本的消息传递协议,最初是为 Ruby, Python和 Perl等脚本语言创建的,用于连接企业级消息代理。由于 STOMP,使不同语言开发的客户端和代理可以相互发送和接收消息。 WebSocket协议有时称为 WebTCP。以此类推, STOMP被称为 WebHTTP。它定义了一些映射到 WebSocket帧的帧类型,例如 CONNECT, SUBSCRIBE, UNSUBSCRIBE, ACK或 SEND。一方面,这些命令非常便于管理通信,另一方面,它们允许我们实现具有更复杂功能的解决方案,如消息确认。 为了构建 WebSocket服务器端,我们将利用 SpringBoot框架,该框架使得在Java中开发独立程序和Web应用程序更快。 SpringBoot包含 spring-WebSocket模块,该模块与 JavaWebSocketAPI标准(JSR-356)兼容。 使用 SpringBoot实现 WebSocket服务器端并不是一项非常复杂的任务,只包含几个步骤,我们将逐一介绍。 步骤1:首先,添加WebSocket库依赖项。 如果计划使用 JSON格式传输消息,则可能还需要包含 GSON或 Jackson依赖项。您还可能还需要一个安全框架,例如 SpringSecurity。 步骤2:然后,可以配置 Spring启用 WebSocket和 STOMP消息传递。 } 图:服务器端如何处理消息 回到上面的代码段 - 可能你已经注意到对方法 withSockJS()的调用——它启用了 SockJS后备选项。简而言之,即使互联网浏览器不支持 WebSocket协议,它也会让我们的 WebSockets工作。我将进一步详细讨论这个主题。 还有一件事需要澄清——为什么我们在端点上调用 setAllowedOrigins()方法。一般是必需的,因为 WebSocket和 SockJS的默认行为是仅接受同源请求。因此,如果客户端和服务端处于不同的域,则需要调用此方法允许它们之间的通信。 它将向订阅特定主题的所有用户广播收到的消息。这是一个将消息发送到目标地址 /topic/news的示例方法。 也可以使用 SimpMessagingTemplate而不是注解 @SendTo,您可以在控制器内自动装配(Autowired)。 在后面的步骤中,可能需要添加一些其他类来保护端点,例如 SpringSecurity框架中的 ResourceServerConfigurerAdapter或 WebSecurityConfigurerAdapter。此外,实现消息模型通常是有益的,这样传输的 JSON可以映射成对象。 客户端实现是一项更简单的任务。 步骤1:装配 SpringSTOMP客户端 步骤2:打开连接 此操作完成后,可以将消息发送到目的地。该消息将发送给所有订阅主题的用户。 以下方法也可以订阅消息 有时需要向特定用户发送消息(例如,在实现聊天时)。然后,客户端和服务器端必须使用专用于此私人会话的单独目标地址。可以通过将唯一标识符附加到通用地址来创建目标地址的名称,例如 /queue/chat-user123。 HTTP会话或 STOMP会话标识符可用于此目的。 Spring使发送私人消息变得更加容易。我们只需要使用 @SendToUser注释 Controller的方法。然后,目标地址将由 UserDestinationMessageHandler处理,它依赖于会话标识符。在客户端,当客户端订阅以 /user为前缀的目标地址时,此目标地址将转换为此用户唯一的目标地址。在服务器端,根据用户的 Principal解析用户目标地址。 服务器端使用 @SendToUser注解示例代码: 或者可以使用 SimpMessagingTemplate: 现在让我们看看如何实现一个能够接收私有消息的 JavaScript(SockJS)客户端,该客户端可以接收上面的示例中的Java代码发送的消息。值得一提的是, WebSockets是 HTML5规范的一部分,并且受到大多数现代浏览器的支持(从版本10开始, InternetExplorer支持它们)。 您可能已经注意到,要接收私人消息,客户端需要订阅前缀为 /user的目标地址 /queue/greetings。它不必担心任何唯一标识符。但是,在客户端登录应用程序之前,服务器端必须初始化 Principal对象。 许多 Web应用程序使用基于 cookie的身份验证,例如,我们可以使用 SpringSecurity限制已登录的用户访问某些页面或控制器限制。然后,通过基于cookie的HTTP会话维护用户上下文安全,该会话稍后与为该用户创建的 WebSocket或 SockJS会话相关联。 WebSocket端点可以像任何其他请求一样受到保护,例如,在 Spring WebSecurityConfigurerAdapter中的实现。 如今, Web应用程序通常使用 REST API作为后端,使用 OAuth/JWT令牌进行用户身份验证和授权。 WebSocket协议未描述服务器在 HTTP握手期间如何对客户端进行身份验证。实际上,标准 HTTP头(例如,授权)用于此目的。不幸的是,并非所有 STOMP客户端都支持它。 Spring的 STOMP客户端允许为握手设置标头: 但是 SockJS的JavaScript客户端不支持使用 SockJS请求发送授权请求头(Authorization)。但是,它允许发送可用于传递令牌的查询参数。此方法需要在服务器端编写自定义代码,该代码将从查询参数中读取令牌并对其进行验证。特别重要的是确保令牌不与请求一起记录(或日志受到良好保护),因为这可能会导致严重的安全违规。 与 WebSocket的集成可能并不总是尽如人意。某些浏览器(例如,IE 9)不支持 WebSocket。更重要的是,限制性代理可能使HTTP升级变得不可能,或者它们切断了打开太久的连接。在这种情况下,SockJS就会伸出援手。 SockJS传输分为三大类: WebSocket, HTTPStreaming和 HTTPLongPolling。通信从 SockJS发送 GET /info以从服务器获取基本信息开始。 SockJS根据响应决定使用的哪种传输方式。第一个选择是 WebSocket。如果不支持,则尽可能使用 Streaming。如果 Streaming也不可用,则选择轮询作为传输方法。 虽然这种设置有效,但它并不是“最佳”。 SpringBoot允许您使用任何具有 STOMP协议的完整消息系统(例如,ActiveMQ,RabbitMQ),并且外部代理可以支持更多 STOMP操作(例如,确认,租借)而不是我们使用的简单代理。 STOMPOverWebSocket提供有关 WebSocket和 STOMP协议的信息。它列出了处理 STOMP协议的消息传递系统,可能是在生产中使用的更好的解决方案。特别是由于请求数量很大,消息代理需要进行集群(Spring的简单消息代理不适合集群)。然后,不需要在 WebSocketConfig中启用简单代理,而是需要启用 Stomp代理中继,该中继将消息转发到外部消息代理和从外部消息代理转发消息。总而言之,外部消息代理可以帮助您构建更具伸缩性和可靠性的解决方案。 原文链接:https://www.toptal.com/java/stomp-spring-boot-websocket 作者:Tomasz D?browski 译者:Emma 推荐阅读:Spring Boot&ES记录日志 Spring Boot实现带STOMP的WebSocket 标签:set 标识符 依赖 优秀 start 多个 定义 stomp 缺点 原文地址:https://blog.51cto.com/14901350/2525083
帮助每位开发者更优秀地成长
WebSocket协议是应用程序处理实时消息的方法之一。最常见的替代方案是长轮询(long polling)和服务器推送事件(server-sent events)。这些解决方案中的每个都有其优缺点。在本文中,我将向您展示如何使用 SpringBoot实现 WebSocket。我将介绍服务器端和客户端设置,使用 WebSocket协议之上的 STOMP进行相互通信。1.WebSocket和STOMP协议
2.服务端:Spring Boot和WebSocket
Configuration
@EnableWebSocketMessageBroker
public
class
WebSocketConfig
implements
WebSocketMessageBrokerConfigurer
{
@Override
public
void
registerStompEndpoints
(
StompEndpointRegistry
registry
)
{
registry
.
addEndpoint
(
"/mywebsockets"
)
.
setAllowedOrigins
(
"mydomain.com"
).
withSockJS
();
}
@Override
public
void
configureMessageBroker
(
MessageBrokerRegistry
config
){
config
.
enableSimpleBroker
(
"/topic/"
,
"/queue/"
);
config
.
setApplicationDestinationPrefixes
(
"/app"
);
}
configureMessageBroker主要做两件事情:
步骤3:实现处理用户请求的控制器
@MessageMapping
(
"/news"
)
@SendTo
(
"/topic/news"
)
public
void
broadcastNews
(
@Payload
String
message
)
{
return
message
;
}
@MessageMapping
(
"/news"
)
public
void
broadcastNews
(
@Payload
String
message
)
{
this
.
simpMessagingTemplate
.
convertAndSend
(
"/topic/news"
,
message
)
}
3.WebSocket客户端构建
@Autowired
private
WebSocketStompClient
stompClient
;
StompSessionHandler
sessionHandler
=
new
CustmStompSessionHandler
();
StompSession
stompSession
=
stompClient
.
connect
(
loggerServerQueueUrl
,
sessionHandler
).
get
();
stompSession
.
send
(
"topic/greetings"
,
"Hello new user"
);
session
.
subscribe
(
"topic/greetings"
,
this
);
@Override
public
void
handleFrame
(
StompHeaders
headers
,
Object
payload
)
{
Message
msg
=
(
Message
)
payload
;
logger
.
info
(
"Received : "
+
msg
.
getText
()+
" from : "
+
msg
.
getFrom
());
}
@MessageMapping
(
"/greetings"
)
@SendToUser
(
"/queue/greetings"
)
public
String
reply
(
@Payload
String
message
,
Principal
user
)
{
return
"Hello "
+
message
;
}
String
username
=
this
.
simpMessagingTemplate
.
convertAndSendToUser
();
username
,
"/queue/greetings"
,
"Hello "
+
username
);
function connect
()
{
var socket
=
new
SockJS
(
‘/greetings‘
);
stompClient
=
Stomp
.
over
(
socket
);
stompClient
.
connect
({},
function
(
frame
)
{
stompClient
.
subscribe
(
‘/user/queue/greetings‘
,
function
(
greeting
)
{
showGreeting
(
JSON
.
parse
(
greeting
.
body
).
name
);
});
});
}
function sendName
()
{
stompClient
.
send
(
"/app/greetings"
,
{},
$
(
"#name"
).
val
());
}
4.WebSocket安全
WebSocketHttpHeaders
handshakeHeaders
=
new
WebSocketHttpHeaders
();
handshakeHeaders
.
add
(
principalRequestHeader
,
principalRequestValue
);
5.SockJS后备选项
6.生产中使用WebSocket
上篇好文:Java泛型构造函数
上一篇:Java泛型构造函数
文章标题:Spring Boot实现带STOMP的WebSocket
文章链接:http://soscw.com/index.php/essay/70152.html