安全的 WebSocket 鉴权必须在 HTTP Upgrade 握手阶段完成,不可延至连接建立后;最通用方案是 URL 传短期专用 Token(需 encodeURIComponent 编码、15 分钟有效期),服务端须在校验签名、exp、iat 及 Redis 黑名单后立即拒绝非法请求;生产环境推荐 Nginx 前置鉴权并透传 token 至 header,同时禁用 Sec-WebSocket-Protocol 传凭据、杜绝明文 ws 和连接后鉴权。

安全的 WebSocket 鉴权不能依赖连接建立后的消息,必须在 HTTP Upgrade 握手阶段完成——此时连接尚未真正切换为 WebSocket 帧通信,服务端还能返回 401/403 或直接拒绝,避免未授权连接占用资源。
URL 中传短期专用 Token(浏览器兼容性最好)
这是目前最通用、最稳妥的方案。浏览器原生 WebSocket 构造函数只接受 URL 字符串,不支持 headers,因此把 Token 放 query 参数是唯一可靠路径:
- 前端拼接时必须用
encodeURIComponent()编码,防止 JWT 中的+、/、=被破坏 - Token 必须是短期有效的专用凭证(建议 15 分钟),不可复用登录态长期 token
- 服务端需在
connection事件或更早的verifyClient钩子中解析并校验:签名、exp、iat、发行方,并查 Redis 黑名单确认未被吊销 - Nginx/CDN 需配置透传 query string,否则
req.url拿不到参数
代理层前置鉴权(生产环境推荐)
把鉴权逻辑前移到网关,减轻业务服务压力,也更符合边缘安全原则:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 前端仍走 URL 传参,Nginx 提取
$arg_token,调用内部鉴权接口(如 JWT 校验 + Redis 查黑名单)同步验证 - 验证失败直接返回 401,不执行
proxy_pass和upgrade,连接根本不会到达后端 - 可配合
proxy_set_header X-WS-Auth $arg_token,让后端从 header 读取(避免日志泄露),同时保持语义清晰 - 动态 Token 前置获取更安全:先请求
/api/v1/ws-token(受 HTTPS+CSRF+Cookie 保护),拿到一次性、绑定设备指纹的短期 Token,再用于 WS 连接
连接内 Token 续期机制(实现无感长连)
WebSocket 连接建立后无法更新鉴权凭据,但可通过协议内消息维持会话有效性:
立即学习“Java免费学习笔记(深入)”;
- 服务端用 Redis 存储每个连接的
session_id → user_id + expires_at,TTL 与 Token 有效期一致 - 客户端在 Token 到期前 2 分钟,主动发
{"type":"refresh_auth","token":"..."}消息 - 服务端校验新 Token 合法后,更新 Redis 中该 session 的过期时间,并返回确认;失败则通知客户端重连
- 心跳包(ping/pong)可顺带检查 TTL,自动触发续期或踢出过期连接
避开常见陷阱
这些做法看似可行,实则存在安全或兼容性风险:
-
不用
Sec-WebSocket-Protocol传 token:它是 RFC 定义的子协议协商字段,不是认证通道,强行塞 token 违反规范、易被拦截、且部分代理会过滤或改写 -
不在
@OnOpen或首条message中做首次鉴权:此时连接已建立,TCP 和内存资源已被占用,恶意连接已可发起攻击 - 不依赖 fetch 模拟握手:fetch 不支持 Upgrade 协议,无法真正建立 WebSocket 连接,只是发了个普通 HTTP 请求
- HTTPS 是硬性前提:明文传输的 Token 会被中间人截获,URL 中的参数尤其危险

















