hl-api-changelog/changelogs-v2/2026-07/66_管理后台消息SSE鉴权重连与旧会话恢复-前端待处理-管理后台.md

9.9 KiB

【前端待处理·管理后台】管理后台消息 SSE 鉴权重连与旧会话恢复

模块:管理后台全局消息 / 在线状态 / 聊天信令 | 服务hl-gateway + hl-user-service
类型:前端待处理 + 联调告知 | 更新时间2026-07-19
影响范围:管理后台全局 SSE 连接、顶部未读角标、聊天、在线状态与抢单池信令
状态:后端已完成根因定位;前端尚未修复;接口契约未变

1. 结论与处理优先级

⚠️ 2026-07-19 测试环境启用 SSE 连接角色一致性校验后,发布前已签发且仍在有效期内的旧登录会话可能缺少当前角色标记。此时网关能够识别 access token,但用户服务会拒绝建立 SSE,前端当前实现会持续使用同一登录会话无限重连。

  • 接口 URL、HTTP 方法、事件结构均未修改。
  • 这不是 token Query 参数名写错;当前前端 URL 拼接方式与网关读取方式一致。
  • 用户立即恢复方式:退出当前账号,重新登录并选择当前角色,再建立 SSE。
  • 前端必须处理Token/角色变化时主动重建连接、限制连续失败重试、给出重新登录提示,并消除默认 message 事件的重复注册。
  • 本次现象包含后端发布前旧会话兼容问题;前端改造用于正确管理连接生命周期和避免无限重试,不代表把后端兼容责任转移给前端。

2. 当前接口契约

GET /ws/admin-msg/stream?token=<accessToken>
Accept: text/event-stream
  • 认证:管理后台 access token。
  • 当前使用原生 EventSource,浏览器 API 不能自定义 Authorization Header,因此现有实现通过 Query 参数传递 token。
  • token 必须使用当前 Store 中的 access token,并通过 encodeURIComponent 做 URL 编码。
  • 成功建连后,请求应长期保持 Pending,响应类型为 text/event-stream
  • 首个握手事件:
event: connected
data: ok
  • 后续仍沿用现有具名事件,包括 unread-countim-chatim-chat-readpresencegrab-pool-changed;本次没有修改事件数据结构。

3. 已确认的问题链路

3.1 旧登录会话与新角色标记不兼容

测试环境运行链路已确认:

  1. 网关可以从 ?token= 读取并校验管理后台 JWT。
  2. 网关向用户服务转发可信的管理员身份及角色信息。
  3. 用户服务在下发任何 SSE 数据前校验“连接角色是否仍为当前登录角色”。
  4. 发布前签发的旧登录会话没有初始化新角色标记时,校验按安全策略失败并关闭连接。
  5. 新登录或重新选择角色会重新写入角色标记,因此重新登录后可恢复。

该校验采用 fail-closed失败时拒绝策略,目的是避免角色切换后旧 Token 继续接收不属于当前角色的消息。

3.2 前端当前会无限重试同一失败会话

当前 src/composables/useAdminMessageSSE.jsEventSource.onerror 后执行关闭和指数退避,但没有连续失败上限,也没有触发重新登录或鉴权恢复流程。

原生 EventSource.onerror 不暴露 HTTP 状态码和响应正文,前端不能仅凭 onerror 精确区分 401/403、服务异常和临时断网。因此不能把所有错误都直接判定为 Token 失效,但必须限制无休止重连。

3.3 默认 message 事件被重复注册

当前实现同时注册:

es.onmessage = handleMessage
es.addEventListener('message', handleMessage)

两种写法都会监听默认 message 事件,并不是互斥兜底。后端发送默认 message 时,同一数据可能被处理两次,必须只保留一种注册方式。

4. 用户立即恢复步骤

  1. 关闭当前页面产生的旧 SSE 连接。
  2. 正常退出管理后台。
  3. 重新登录,并重新选择当前需要使用的角色。
  4. 进入主布局后重新建立 /ws/admin-msg/stream
  5. 在浏览器 Network 中确认请求保持 Pending,并收到一次 connected 事件。

不要通过手工复制、修改或在地址栏粘贴完整 Token 的方式恢复连接。

5. 【前端·管理后台】适配清单

5.1 让 SSE 生命周期跟随登录凭证和角色

  • 监听 userStore.token 变化;值变化时先关闭旧 EventSource,再使用最新 Token 建立唯一的新连接。
  • 角色切换成功并更新 Token 后,立即重建 SSE,不等待旧连接自行报错。
  • 登出、主布局卸载或 Token 被清空时,关闭连接、清理重连定时器并禁止再次拉起。
  • 保证全局最多只有一个管理后台消息 SSE 实例,避免布局重复挂载造成多连接。
  • 重建连接时始终从 Store 现取 Token,不缓存旧登录会话中的 Token 字符串。

5.2 限制连续失败,避免无限重连

  • 保留指数退避和最大间隔,但增加“连续失败次数/总时长”上限。
  • 仅在收到后端 connected 事件后清零连续失败计数;EventSource.onopen 不能作为鉴权成功依据,也不能清零计数。
  • 达到上限后停止自动重试,并显示中性、可操作的提示,例如“消息连接连续失败,请检查网络或重新登录”。
  • 用户完成重新登录、Token 刷新、角色切换或主动点击重试后,才开启新一轮连接。
  • 临时断网恢复后仍允许重连;可结合 online 事件或显式重试入口恢复,而不是永久静默失效。

注意:由于原生 EventSource 无法在 onerror 中读取响应状态,前端不要根据一次 onerror 立即清空登录态。需要使用连续失败阈值,并结合普通鉴权接口结果或既有 Token 刷新状态判断。

5.3 消除重复消息处理

  • es.onmessagees.addEventListener('message', ...) 只保留一种。
  • connectedunread-countim-chatim-chat-readpresencegrab-pool-changed 等具名事件继续分别注册。
  • 验证单条默认 message、聊天信令和未读数信令都只被业务层消费一次。

5.4 失败信息与联调反馈

  • 前端提示中不要展示 Token、完整 SSE URL、Cookie 或管理员标识。
  • 如重新登录后仍失败,只反馈发生时间、页面、错误 messageX-Trace-Id
  • 若 Network 原始响应确实为“未提供有效的Token”,请附 X-Trace-Id 交后端继续检查路由/拦截器链;不要附 Token。

6. 验收场景

场景 期望结果
重新登录后首次进入主布局 只建立 1 条 SSE;请求保持 Pending;收到 1 次 connected
access token 刷新 旧连接关闭,使用新 Token 只重建 1 次
切换管理后台角色 旧角色连接立即关闭;新角色 Token 建立新连接;不接收旧角色后续数据
发布前旧会话无法建连 退避重试达到阈值后停止,并明确提示重新登录;不无限刷请求
只触发 onopen、未收到 connected、随后触发 onerror 仍累计连续失败次数,不得被 onopen 反复清零
临时断网后恢复 在受控退避或用户重试后恢复连接,不产生并发 SSE
收到默认 message 同一事件只处理 1 次
正常登出 SSE 和重连定时器均被清理,退出页不再发起连接
重新登录后仍失败 联调材料仅包含时间、页面、错误消息、X-Trace-Id,不包含 Token

7. 后端状态与边界

  • 当前接口路径、Query 参数名和 SSE 事件结构未变,不需要前端调整数据模型。
  • 新登录/角色切换链路会写入当前角色标记,重新登录是当前可用的恢复手段。
  • 发布前旧会话没有迁移标记是本次问题的触发条件;后端尚未交付旧会话兼容补丁。
  • 角色一致性校验必须保留,不能为了兼容旧会话而允许旧角色 Token 接收消息。
  • 若后续改为一次性 SSE Ticket、Fetch Streaming 或其他不在 URL 中携带 access token 的方案,将另发接口契约,不在本次前端适配范围内。

8. 安全要求

  • 禁止把完整 Token、带 Token 的完整 SSE URL、Cookie 或真实管理员信息写入 Issue、PR、Changelog、日志和截图。
  • Token 一旦通过聊天、工单或截图暴露,应立即停止使用和传播,通知后端/运维按当前鉴权策略显式吊销或拒绝该旧 Token,并验证它已无法访问;随后重新登录获取新 Token。
  • 重新登录只是恢复 SSE 和获取新 Token,不等于旧 JWT 已自动吊销;尤其在仅校验 JWT 签名的环境中,必须单独完成旧 Token 的失效处置。
  • 不得在前端代码中硬编码 Token,也不得把 Token 写入错误上报或埋点参数。

9. 影响范围

文件/能力 说明
src/composables/useAdminMessageSSE.js 连接、重连、事件监听和清理逻辑
src/layouts/BasicLayout.vue 主布局挂载、登出和 SSE 生命周期
角色切换流程 Token 更新后主动重建 SSE
顶部未读角标、聊天、在线状态、抢单池信令 共用同一 SSE,需防止连接缺失或事件重复消费

10. 发布说明

  • 本文是前端联调和修复通知,不代表已修改或发布前端代码。
  • 本文没有包含任何真实 Token、管理员 ID、Cookie 或其他敏感信息。
  • 前端完成后应在 mmg/hl-ui 走自身 Issue、分支、PR、测试和发布流程。

11. 相关历史契约

文档 当前说明
内部员工站内信收件箱 + SSE 实时推送 SSE 路径、Query 鉴权和事件契约仍有效;其中“断线自动重连”的建议被本文补充为有上限的受控重连,鉴权持续失败时不得无限请求
切角色 / 刷新令牌原子保存 tokenrefreshToken 原子保存要求仍有效;保存新 access token 后还必须关闭旧 SSE 并主动重建