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 # 父级 POM3. 核心组件说明
3.1 WebSocket 配置
文件路径: com.zs.config.WebSocketConfig
核心配置:
- 连接端点:
/ws - 支持跨域:
setAllowedOriginPatterns("*") - 消息代理:
- 广播前缀:
/topic - 点对点前缀:
/queue - 应用前缀:
/app - 用户前缀:
/user
- 广播前缀:
- 认证拦截器:
AuthChannelInterceptor
3.2 认证机制
文件路径: com.zs.interceptor.AuthChannelInterceptor
- 只处理
CONNECT命令 - 从请求头获取
Authorization和Tenant-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. 认证流程
- 客户端连接
/ws端点,携带Authorization和Tenant-ID头 AuthChannelInterceptor拦截CONNECT命令- 验证 JWT Token 有效性
- 从 Redis 获取用户信息
- 设置
TenantAwarePrincipal到会话 - 连接成功后,
WebSocketEventListener记录用户上线
9. 在线用户管理
9.1 上线流程
- 客户端成功连接 WebSocket
SessionConnectEvent事件触发- 解析用户信息
- 调用
WebSocketSessionManager.addUser()存储用户信息到 Redis
9.2 下线流程
- 客户端断开连接
SessionDisconnectEvent事件触发- 获取用户身份信息
- 调用
WebSocketSessionManager.removeUser()清理 Redis 中的用户信息
10. 配置参数
| 配置项 | 说明 | 默认值 |
|---|---|---|
spring.websocket.endpoint | WebSocket 连接端点 | /ws |
spring.websocket.allowed-origins | 允许的跨域来源 | * |
spring.websocket.enable-sockjs | 是否启用 SockJS | false |
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 缓存过期
- 服务器重启