WebSocket连接必须在HandshakeInterceptor的beforeHandshake阶段校验Token,通过Header(Authorization)或URL参数获取并解析JWT,失败返回false;@MessageMapping中无法动态鉴权,需在握手时存用户信息到session属性。

WebSocket连接建立时如何校验Token
Spring Boot的WebSocket不走HTTP拦截器链,HandlerInterceptor对WebSocketHandler完全无效。必须在握手阶段(handshake)介入,用HandshakeInterceptor提取并验证Token。
常见错误是把Token塞在HTTP Header里然后试图在afterConnectionEstablished里解析——此时已错过校验时机,连接早就建立了。
- Token应放在HTTP请求Header(如
Authorization: Bearer xxx)或URL参数(如ws://host/ws?token=xxx),前者更安全 - 重写
beforeHandshake方法,在其中调用自定义Token校验逻辑(如解析JWT、查Redis黑名单) - 校验失败必须返回
false,并可选地向attributes里放错误信息供后续日志追踪 - 注意:Spring Boot 2.6+默认禁用
withSockJS(),若用SockJS需额外配置CORS和session策略
为什么不能在@MessageMapping方法里做权限检查
WebSocket消息处理(@MessageMapping)属于STOMP层,此时连接已稳定,用户身份也已“缓存”在SimpMessagingTemplate上下文中。但一旦连接建立,后续所有@MessageMapping方法都共享同一个Principal——它来自握手时的认证结果,不会自动刷新。
典型陷阱:用户Token过期后仍能发消息,因为Principal没变;或者多个用户共用一个连接(如前端未正确断开),导致权限错乱。
- STOMP协议本身不强制每次消息携带凭证,所以
@MessageMapping里拿不到原始Token - 若真需动态鉴权(如操作敏感资源),应在业务方法内手动调用
SecurityContextHolder.getContext().getAuthentication()再校验Token有效性 - 更稳妥的做法是:握手时把用户ID/角色存入
WebSocketSession.getAttributes(),后续消息处理直接读取,避免重复解析JWT
HandshakeInterceptor中解析JWT Token的实操要点
别直接用String.split()切Bearer Token——容易漏空格、大小写不一致或无前缀。Spring Security的JwtDecoder也不适用于握手阶段,因为它依赖SecurityContext,而握手时尚未初始化。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
推荐用io.jsonwebtoken:jjwt-api + jjwt-impl轻量解析,不耦合Spring Security。
- 从
headers.get("Authorization")取值,用StringUtils.startsWithIgnoreCase(header, "Bearer ")判断前缀 - 提取Token后,用
Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token)校验签名和过期时间 - 注意:密钥必须与签发方一致;若用RSA,需传
PublicKey而非字符串 - 解析异常(如
ExpiredJwtException、SignatureException)必须捕获并返回false,否则会抛出500且连接静默失败
前端WebSocket连接时传Token的兼容写法
浏览器原生WebSocket构造函数不支持自定义Header,所以Header方式只适用于SockJS或Spring WebFlux场景。实际项目中,90%以上得靠URL参数传Token。
但URL参数有长度限制和日志泄露风险,必须配合后端二次校验和短期有效期(如5分钟)。
- 前端连接写法:
new WebSocket("ws://localhost:8080/ws?token=" + encodeURIComponent(token)) - 后端在
beforeHandshake中用uri.getQuery()解析参数,别用request.getParameter()(非Servlet环境不可用) - 若用Vue/React,确保Token从Vuex/Pinia或Context中安全读取,避免硬编码或localStorage明文存储
- 生产环境务必启用WSS(TLS),否则Token会在中间人攻击中被截获
真正麻烦的不是怎么写拦截器,而是Token生命周期管理:握手时校验一次,之后整个连接周期内身份就固化了。如果业务要求实时权限变更(比如管理员踢人),得靠服务端主动WebSocketSession.close(),而不是等下次消息才反应过来。

















