Skip to content

WebSocket 模块文档

1. 模块概述

该模块提供了基于 Spring WebSocket 的实时消息通信功能,支持:

  • 多租户隔离
  • 广播消息(/topic)
  • 点对点消息(/queue)
  • 在线用户管理
  • JWT 认证机制

2. 目录结构

zs-websocket/
├── zs-websocket-service/       # 服务实现模块
│   ├── src/main/java/com/zs/
│   │   ├── config/
│   │   │   └── WebSocketConfig.java    # WebSocket 配置
│   │   ├── controller/
│   │   │   ├── OnlineUserController.java  # 在线用户管理
│   │   │   └── WebsocketController.java   # WebSocket 消息处理
│   │   ├── interceptor/
│   │   │   └── AuthChannelInterceptor.java  # 认证拦截器
│   │   ├── listener/
│   │   │   └── WebSocketEventListener.java  # 事件监听器
│   │   ├── manager/
│   │   │   └── WebSocketSessionManager.java  # 会话管理
│   │   ├── model/
│   │   │   ├── params/
│   │   │   │   └── OnlineUserQueryParams.java
│   │   │   └── TenantAwarePrincipal.java  # 租户感知用户身份
│   └── pom.xml
└── pom.xml                     # 父级 POM

3. 核心组件说明

3.1 WebSocket 配置

文件路径: com.zs.config.WebSocketConfig

核心配置:

  • 连接端点:/ws
  • 支持跨域:setAllowedOriginPatterns("*")
  • 消息代理:
    • 广播前缀:/topic
    • 点对点前缀:/queue
    • 应用前缀:/app
    • 用户前缀:/user
  • 认证拦截器:AuthChannelInterceptor

3.2 认证机制

文件路径: com.zs.interceptor.AuthChannelInterceptor

  • 只处理 CONNECT 命令
  • 从请求头获取 AuthorizationTenant-ID
  • 验证 JWT Token 有效性
  • 设置租户感知的用户身份 TenantAwarePrincipal

3.3 会话管理

文件路径: com.zs.manager.WebSocketSessionManager

使用 Redis 管理在线用户:

  • 用户详情存储: Hash 结构,键名 online:user:detail:{sessionId}
  • 租户在线列表: ZSet 结构,按登录时间排序,键名 online:user:list:{tenantId}
  • 有效期:30 分钟

3.4 事件监听

文件路径: com.zs.listener.WebSocketEventListener

  • 连接事件: SessionConnectEvent,用户上线时存储用户信息
  • 断开事件: SessionDisconnectEvent,用户下线时清理用户信息

3.5 消息处理控制器

文件路径: com.zs.controller.WebsocketController

3.5.1 广播消息

java
@MessageMapping("/sendToAll")
public void sendToAll(@Payload String message, TenantAwarePrincipal principal) {
    String tenantId = principal.getTenantId();
    simpMessagingTemplate.convertAndSend("/topic/" + tenantId + "/message", message);
}
  • 客户端发送地址: /app/sendToAll
  • 客户端订阅地址: /topic/{tenantId}/message

3.5.2 点对点消息

java
@MessageMapping("/sendToUser/{receiverId}")
public void sendToUser(@DestinationVariable String receiverId, @Payload String message, TenantAwarePrincipal principal) {
    String tenantId = principal.getTenantId();
    String fullReceiverId = tenantId + ":" + receiverId;
    simpMessagingTemplate.convertAndSendToUser(fullReceiverId, "/queue/" + tenantId + "/message", message);
}
  • 客户端发送地址: /app/sendToUser/{receiverId}
  • 客户端订阅地址: /user/queue/{tenantId}/message

4. 服务层实现

4.1 WebsocketService

文件路径: com.zs.service.WebsocketService

提供了与控制器相同的消息发送能力,供内部服务调用。

4.2 OnlineUserService

管理在线用户信息,包括:

  • 查询在线用户列表
  • 获取在线用户详情
  • 统计在线用户数量

5. API 接口

5.1 WebsocketApi

文件路径: websocket.WebsocketApi

java
public interface WebsocketApi {
    // 给所有人发送消息
    void sendMessage(String message);
    // 给某个用户发送消息
    void sendMessageToUser(String receiverId, String message);
}

该接口为公共 API,供其他模块调用。

6. 客户端接入指南

6.1 连接建立

javascript
const socket = new WebSocket('ws://localhost:8080/ws?access_token=YOUR_JWT_TOKEN');

6.2 STOMP 客户端示例

javascript
const client = Stomp.over(socket);

client.connect({
    'Authorization': 'Bearer YOUR_JWT_TOKEN',
    'Tenant-ID': 'YOUR_TENANT_ID'
}, () => {
    // 订阅广播消息
    client.subscribe('/topic/YOUR_TENANT_ID/message', (message) => {
        console.log('Received broadcast message:', message.body);
    });
    
    // 订阅个人消息
    client.subscribe('/user/queue/YOUR_TENANT_ID/message', (message) => {
        console.log('Received private message:', message.body);
    });
});

6.3 发送消息

javascript
// 发送广播消息
client.send('/app/sendToAll', {}, JSON.stringify({ content: 'Hello everyone!' }));

// 发送点对点消息
client.send('/app/sendToUser/RECEIVER_USER_ID', {}, JSON.stringify({ content: 'Hello!' }));

7. 多租户隔离机制

  • 所有消息通道都包含租户 ID 前缀
  • 用户身份包含租户信息 TenantAwarePrincipal
  • 在线用户按租户分组存储
  • 消息发送时自动附加租户 ID

8. 认证流程

  1. 客户端连接 /ws 端点,携带 AuthorizationTenant-ID
  2. AuthChannelInterceptor 拦截 CONNECT 命令
  3. 验证 JWT Token 有效性
  4. 从 Redis 获取用户信息
  5. 设置 TenantAwarePrincipal 到会话
  6. 连接成功后,WebSocketEventListener 记录用户上线

9. 在线用户管理

9.1 上线流程

  1. 客户端成功连接 WebSocket
  2. SessionConnectEvent 事件触发
  3. 解析用户信息
  4. 调用 WebSocketSessionManager.addUser() 存储用户信息到 Redis

9.2 下线流程

  1. 客户端断开连接
  2. SessionDisconnectEvent 事件触发
  3. 获取用户身份信息
  4. 调用 WebSocketSessionManager.removeUser() 清理 Redis 中的用户信息

10. 配置参数

配置项说明默认值
spring.websocket.endpointWebSocket 连接端点/ws
spring.websocket.allowed-origins允许的跨域来源*
spring.websocket.enable-sockjs是否启用 SockJSfalse
spring.websocket.broker.topic广播消息前缀/topic
spring.websocket.broker.queue点对点消息前缀/queue
spring.websocket.application-prefix应用消息前缀/app
spring.websocket.user-prefix用户消息前缀/user

11. 常见问题

11.1 连接失败

可能原因

  • JWT Token 无效或已过期
  • 缺少 Tenant-ID
  • 跨域配置问题

11.2 消息发送失败

可能原因

  • 接收者不存在或已离线
  • 租户 ID 不匹配
  • 权限不足

11.3 在线用户数量不准确

可能原因

  • 网络异常导致断开事件未触发
  • Redis 缓存过期
  • 服务器重启