SockJsClient(React) WebSocket握手成功后onMessage未触发,消息接收异常
问题根因及修复方案
核心根因
1. 推送路径与订阅路径不匹配
Spring Stomp 的convertAndSendToUser方法默认会自动在推送路径前拼接/user/{用户名}前缀,你当前服务端实际推送的完整路径为:/user/{pushToken}/ws-push-notification/item/{pushToken}
但前端订阅的路径为ws-push-notification/item/{pushToken},二者完全不匹配,因此无法触发回调。
2. 路径格式不符合规范
STOMP 协议的订阅路径默认需要以斜杠/开头,你前端定义的 topic 缺少开头的斜杠。
3. 身份映射逻辑错误
convertAndSendToUser的第一个参数是 Spring Security 认证上下文中的用户名,不是 SessionId,你直接传入 pushToken 作为用户名,需要确保项目的 WebSocket 认证逻辑已经将 pushToken 映射为当前连接的用户 Principal 名称。
具体修复步骤
- 方案一:调整前端订阅路径,补全前缀即可
// 修改前 const topic = "ws-push-notification/item/" + props.pushToken; // 修改后 const topic = "/user/ws-push-notification/item/" + props.pushToken;
- 方案二:改用路径直接推送,避免用户身份映射逻辑,修改服务端推送代码
public void pushNotification(String pushToken, PushNotificationDto notificationDto) { // 直接按完整路径推送,不需要额外处理用户和session头 template.convertAndSend("/ws-push-notification/item/" + pushToken, notificationDto); }
对应前端订阅路径修改为:
const topic = "/ws-push-notification/item/" + props.pushToken;
- 方案三:保留
convertAndSendToUser用法的适配方案
需要配置 WebSocket 握手拦截器,在连接建立阶段将 pushToken 设置为当前连接的 Principal:
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws-push-notifications") .setAllowedOrigins(allowedOrigin) // 添加握手拦截器设置Principal .addInterceptors(new HandshakeInterceptor() { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // 从请求参数或者header中获取pushToken设置为Principal String pushToken = request.getHeaders().getFirst("push-token"); if (pushToken != null) { attributes.put("principal", () -> pushToken); } return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } }) .withSockJS(); }
内容的提问来源于stack exchange,提问作者huseyinkadioglu
相关产品推荐
相关产品推荐

