必须放弃纯无状态设计,改用服务端状态管理:建连时校验refresh_token并生成短期session_id存Redis,后续消息仅校验session_id存在性,心跳包检查TTL并触发重鉴权。

WebSocket 本身不支持 HTTP 的 Authorization 头或 Cookie 的自动携带,也无法在连接建立后动态插入 Token。要实现“每小时无感刷新长连接鉴权”,核心思路是:不重连、不中断业务,而是通过 WebSocket 协议内自定义消息机制,在连接存活期间主动更新服务端的鉴权状态。关键在于服务端维护 Token 有效性(如 Redis 存储用户会话 + 过期时间),客户端定时发起轻量级“心跳+续期”请求(非 HTTP,走 WebSocket 消息)。
服务端需支持 Token 续期指令
WebSocket 服务端(如 Node.js 的 ws 库或 Java 的 Spring WebSocket)应监听特定消息类型(例如 {"type":"refresh_auth","token":"xxx"}):
- 收到续期消息后,校验新 Token 是否合法(签名、有效期、用户权限等)
- 若校验通过,更新该连接绑定的用户身份上下文(如内存 Map 或 Redis 中的 session 记录),并重置其过期时间(比如设为当前时间 + 1 小时)
- 返回
{"type":"auth_refreshed","expires_at":1717123456}确认成功,客户端据此更新本地计时逻辑 - 若 Token 失效,可返回错误并由客户端决定是否重连(此时才需要重新登录)
客户端定时发送续期消息(非轮询 HTTP)
前端 JavaScript 不要用 setInterval 调 HTTP 接口刷 Token(这和 WebSocket 鉴权无关,且破坏“无感”);而应在 WebSocket 连接打开后,启动一个与 Token 过期节奏匹配的定时器:
- 假设 Token 初始有效期 1 小时,客户端在连接成功后记录
authExpiry = Date.now() + 60 * 60 * 1000 - 在到期前 2 分钟(即
authExpiry - 120000)触发一次send({type:'refresh_auth', token: localStorage.getItem('access_token')}) - 若续期成功,更新
authExpiry = Date.now() + 60 * 60 * 1000,继续下一轮倒计时 - 注意:Token 必须是客户端当前持有的最新有效 Token(如登录后存入 localStorage,刷新后及时更新)
异常处理与降级策略
网络抖动或服务端短暂不可用可能导致续期失败,不能直接断连:
立即学习“Java免费学习笔记(深入)”;
- 设置最多 2 次重试(间隔 5 秒),失败后延长下次续期时间为 5 分钟后(避免雪崩)
- 监听服务端主动推送的
{"type":"auth_expired"}消息(服务端检测到 Token 过期且未续期成功时下发),此时再跳转登录页 - 页面切后台时暂停定时器,切回前台时重新计算剩余时间并恢复,防止时间错乱
Token 获取与存储建议
WebSocket 连接建立前的 Token 来源必须可靠:
- 首次连接时,从 localStorage / sessionStorage 读取;若不存在,说明未登录,不建连
- 登录接口返回新 Token 后,务必同步更新本地存储,并立即通知已存在的 WebSocket 实例(如有)使用新 Token 续期
- 避免将 Token 放在 URL 参数中传给 WebSocket(
new WebSocket('wss://...?token=xxx')),易被日志/代理泄露,且无法动态更新


















