Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息
作者:mmseoamin日期:2024-02-28

🚀 作者主页: 有来技术

🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot

🌺 仓库主页: Gitee 💫 Github 💫 GitCode

💖 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请纠正!

Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,topic,第1张

目录

  • 引言
  • 核心概念
    • 什么是 WebSocket ?
    • 什么是 STOMP ?
    • STOMP On Spring WebSocket
    • WebSocket 服务端(SpringBoot3)
      • 添加依赖
      • WebSocket 配置类
      • WebSocket 监听和发送
      • WebSocket 客户端(Vue3)
        • 安装依赖
        • WebSocket 客户端连接
        • WebSocket 客户端订阅
        • WebSocket 客户端推送
        • WebSocket 测试
          • WebSocket 连接测试
          • WebSocket 广播测试
          • WebSocket 点对点测试
          • 项目源码
          • 结语

            引言

            WebSocket是一种在Web浏览器与Web服务器之间建立双向通信的协议,而Spring Boot提供了便捷的WebSocket支持。本篇博客将介绍如何在Spring Boot 3中整合WebSocket,并使用Vue 3构建简单而强大的实时通信应用。

            核心概念

            什么是 WebSocket ?

            WebSocket是一种在单个TCP连接上提供全双工通信的协议,允许客户端和服务器之间实时双向通信,无需客户端发起请求。其优势在于低延迟和高效率,适用于即时通讯、在线游戏、实时协作编辑等场景。

            什么是 STOMP ?

            STOMP 官网: 官方文档 | 中文文档

            STOMP是一种基于文本的消息传递协议,用于实现消息传递系统之间的互操作性。它简单、跨语言、适应性强,支持多种消息传递模式。通常与WebSocket结合,提供浏览器和服务器之间的实时双向通信。

            STOMP是一种简单的消息传递协议,初衷是为脚本语言(如 Ruby、 Python 和 Perl)和web框架创建一种基于文本的简单异步消息协议。相比于正式诞生于2011年的WebSocket,STOMP在此之前广泛使用了十年以上,并且得到了很多客户端(如stomp.js、Gozirra、stomp.py、stompngo等)、消息代理端(如ActiveMQ、RabbitMQ等)、工具库的支持,目前最新的协议版本为2012年1.2版本。

            具体来说,STOMP是一种基于Frame的协议,每个Frame由一个命令Command、一组Headers和可选的正文Body组成,如下是一个STOMP frame的基本结构示例:

            SEND   //作为COMMAND
            Authorization:Bearer xxx        //作为Headers
            content-type:application/json   //作为Headers
            Hello World!  //消息内容,可以多行,直到^@为止  //作为Body
            ^@ //此Frame结束
            

            STOMP On Spring WebSocket

            spring-framework #websocket-stomp

            Spring Framework 从 4.0.0 加入了spring-websocket 和 spring-messaging 两大模块。

            从Spring文档的篇幅、提供的应用样例以及spring-boot-starter-websocket直接引入了spring-websocket 和 spring-messaging模块(包含了STOMP等相关内容)等各种情况,不难看出基于STOMP做为其消息交互协议的方式,是spring主推的完整的websocket解决方案即 STOMP On Spring WebSocket,即使用STOMP也是spring框架的选择。

            WebSocket 服务端(SpringBoot3)

            添加依赖

            首先,在pom.xml中添加WebSocket依赖:

            
            
                org.springframework.boot
                spring-boot-starter-websocket
            
            

            WebSocket 配置类

            配置 WebSocket 支持实时双向通信和消息传递,使用STOMP协议。提供连接端点 “/ws”,支持跨域WebSocket连接,配置消息代理实现广播和点对点推送。

            package com.youlai.system.config;
            /**
             * WebSocket 配置
             *
             * @author haoxr
             * @since 2.4.0
             */
            @Configuration
            @EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
            @RequiredArgsConstructor
            @Slf4j
            public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
            	
                private final JwtTokenProvider jwtTokenProvider;
                /**
                 * 注册一个端点,客户端通过这个端点进行连接
                 */
                @Override
                public void registerStompEndpoints(StompEndpointRegistry registry) {
                    registry
                            .addEndpoint("/ws")   // 注册了一个 /ws 的端点
                            .setAllowedOriginPatterns("*") // 允许跨域的 WebSocket 连接
                            .withSockJS();  // 启用 SockJS (浏览器不支持WebSocket,SockJS 将会提供兼容性支持)
                }
                /**
                 * 配置消息代理
                 */
                @Override
                public void configureMessageBroker(MessageBrokerRegistry registry) {
                    // 客户端发送消息的请求前缀
                    registry.setApplicationDestinationPrefixes("/app");
                    // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送
                    registry.enableSimpleBroker("/topic", "/queue");
                    // 服务端通知客户端的前缀,可以不设置,默认为user
                    registry.setUserDestinationPrefix("/user");
                }
                
                /**
                 * 配置客户端入站通道拦截器
                 * 

            * 添加 ChannelInterceptor 拦截器,用于在消息发送前,从请求头中获取 token 并解析出用户信息(username),用于点对点发送消息给指定用户 * * @param registration 通道注册器 */ @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(new ChannelInterceptor() { @Override public Message preSend(Message message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); // 如果是连接请求(CONNECT 命令),从请求头中取出 token 并设置到认证信息中 if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) { // 从连接头中提取授权令牌 String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION); // 验证令牌格式并提取用户信息 if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) { try { // 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中 String tokenWithoutPrefix = bearerToken.substring(7); String username = jwtTokenProvider.getUsername(tokenWithoutPrefix); if (StrUtil.isNotBlank(username)) { accessor.setUser(() -> username); return message; } } catch (Exception e) { log.error("Failed to process authentication token.", e); } } } // 不是连接请求,直接放行 return ChannelInterceptor.super.preSend(message, channel); } }); } }

            WebSocket 监听和发送

            package com.youlai.system.controller;
            import com.youlai.system.model.dto.ChatMessage;
            import lombok.RequiredArgsConstructor;
            import lombok.extern.slf4j.Slf4j;
            import org.springframework.messaging.handler.annotation.DestinationVariable;
            import org.springframework.messaging.handler.annotation.MessageMapping;
            import org.springframework.messaging.handler.annotation.SendTo;
            import org.springframework.messaging.simp.SimpMessagingTemplate;
            import org.springframework.web.bind.annotation.RequestMapping;
            import org.springframework.web.bind.annotation.RestController;
            import java.security.Principal;
            /**
             * WebSocket 测试控制器
             *
             * @author haoxr
             * @since 2.3.0
             */
            @RestController
            @RequestMapping("/websocket")
            @RequiredArgsConstructor
            @Slf4j
            public class WebsocketController {
                private final SimpMessagingTemplate messagingTemplate;
                /**
                 * 广播发送消息
                 *
                 * @param message 消息内容
                 */
                @MessageMapping("/sendToAll")
                @SendTo("/topic/notice")
                public String sendToAll(String message) {
                    return "服务端通知: " + message;
                }
                /**
                 * 点对点发送消息
                 * 

            * 模拟 张三 给 李四 发送消息场景 * * @param principal 当前用户 * @param username 接收消息的用户 * @param message 消息内容 */ @MessageMapping("/sendToUser/{username}") public void sendToUser(Principal principal, @DestinationVariable String username, String message) { String sender = principal.getName(); // 发送人 String receiver = username; // 接收人 log.info("发送人:{}; 接收人:{}", sender, receiver); // 发送消息给指定用户 /user/{username}/queue/greeting messagingTemplate.convertAndSendToUser(receiver, "/queue/greeting", new ChatMessage(sender, message)); } }

            WebSocket 客户端(Vue3)

            安装依赖

            # WebSocket 客户端和类型定义
            npm i sockjs-client  
            npm i -D @types/sockjs-client
            # STOMP 协议的 JavaScript 客户端和类型定义
            npm i stompjs
            npm i -D @types/stompjs
            # net 是 Node核心模块之一,用于创建 TCP 服务器和客户端模块
            npm i net -S
            

            WebSocket 客户端连接

            下面是 Websocket 连接部分关键代码,完整代码:websocket.vue

            
            

            WebSocket 客户端订阅

            下面是 Websocket 订阅部分关键代码,完整代码:websocket.vue

            stompClient.subscribe("/topic/notice", (res: any) => {
                console.log("订阅广播成功:" + res.body);
            });
            stompClient.subscribe("/user/queue/greeting", (res) => {
                console.log("订阅点对点成功:" + res.body);
            });
            
            • /topic/notice

              对应服务端广播队列

              Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,image-20231214155237181,第2张

            • /user/queue/greeting

              对应服务端点对点队列,其中 /user/ 前缀是 WebSocketConfig 里通过 registry.setUserDestinationPrefix("/user") 设定的服务端通知客户端的前缀。

              Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,第3张

              WebSocket 客户端推送

              下面是 Websocket 订阅部分关键代码,完整代码:websocket.vue

              function sendToAll() {
                stompClient.send("/app/sendToAll", {},  "亲爱的大冤种们,由于一只史诗级的BUG,系统版本已经被迫回退到了0.0.1。");
                console.log("广播消息发送成功");	
              }
              function sendToUser() {
                stompClient.send("/app/sendToUser/root", {}, "嗨! root 我是 admin ,想和您交个朋友");
                console.log("点对点消息发送成功");	
              }
              
              • /app/sendToAll

                客户端发送的目标地址,指定服务端 @MessageMapping("/sendToAll") 方法处理此消息

                其中 /app 是 WebSocketConfig 里通过 registry.setApplicationDestinationPrefixes("/app") 设定的客户端发送目标地址的前缀

                Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,第4张

              • /app/sendToUser/root

                对应服务端处理消息的方法如下:

                Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,第5张

                WebSocket 测试

                在线测试地址: vue3-element-admin#websocket

                WebSocket 连接测试

                Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,第6张

                WebSocket 广播测试

                Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,topic,第1张

                WebSocket 点对点测试

                Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息,第8张

                项目源码

                GithubGitee
                后端youlai-boot 🍃youlai-boot 🍃
                前端vue3-element-admin 🌺vue3-element-admin 🌺

                结语

                本文深入介绍了在Spring Boot 3中整合WebSocket及Vue 3构建实时通信应用。选择STOMP协议,配置服务端、客户端,实现连接、消息广播和点对点推送。通过在线测试验证整合效果,包括连接、广播和点对点消息。希望读者通过这个实例更好地理解WebSocket在Spring Boot中的应用。