docs: 补充管理后台 SSE 鉴权与重连修复通知
这个提交包含在:
父节点
7c7914bd34
当前提交
eb96028029
@ -0,0 +1,160 @@
|
||||
# 【前端待处理·管理后台】管理后台消息 SSE 鉴权重连与旧会话恢复
|
||||
|
||||
> **模块**:管理后台全局消息 / 在线状态 / 聊天信令 | **服务**:`hl-gateway` + `hl-user-service`<br>
|
||||
> **类型**:前端待处理 + 联调告知 | **更新时间**:2026-07-19<br>
|
||||
> **影响范围**:管理后台全局 SSE 连接、顶部未读角标、聊天、在线状态与抢单池信令<br>
|
||||
> **状态**:后端已完成根因定位;前端尚未修复;接口契约未变
|
||||
|
||||
## 1. 结论与处理优先级
|
||||
|
||||
> ⚠️ 2026-07-19 测试环境启用 SSE 连接角色一致性校验后,发布前已签发且仍在有效期内的旧登录会话可能缺少当前角色标记。此时网关能够识别 access token,但用户服务会拒绝建立 SSE,前端当前实现会持续使用同一登录会话无限重连。
|
||||
|
||||
- 接口 URL、HTTP 方法、事件结构均未修改。
|
||||
- 这不是 `token` Query 参数名写错;当前前端 URL 拼接方式与网关读取方式一致。
|
||||
- **用户立即恢复方式**:退出当前账号,重新登录并选择当前角色,再建立 SSE。
|
||||
- **前端必须处理**:Token/角色变化时主动重建连接、限制连续失败重试、给出重新登录提示,并消除默认 `message` 事件的重复注册。
|
||||
- 本次现象包含后端发布前旧会话兼容问题;前端改造用于正确管理连接生命周期和避免无限重试,不代表把后端兼容责任转移给前端。
|
||||
|
||||
## 2. 当前接口契约
|
||||
|
||||
```http
|
||||
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`。
|
||||
- 首个握手事件:
|
||||
|
||||
```text
|
||||
event: connected
|
||||
data: ok
|
||||
```
|
||||
|
||||
- 后续仍沿用现有具名事件,包括 `unread-count`、`im-chat`、`im-chat-read`、`presence` 和 `grab-pool-changed`;本次没有修改事件数据结构。
|
||||
|
||||
## 3. 已确认的问题链路
|
||||
|
||||
### 3.1 旧登录会话与新角色标记不兼容
|
||||
|
||||
测试环境运行链路已确认:
|
||||
|
||||
1. 网关可以从 `?token=` 读取并校验管理后台 JWT。
|
||||
2. 网关向用户服务转发可信的管理员身份及角色信息。
|
||||
3. 用户服务在下发任何 SSE 数据前校验“连接角色是否仍为当前登录角色”。
|
||||
4. 发布前签发的旧登录会话没有初始化新角色标记时,校验按安全策略失败并关闭连接。
|
||||
5. 新登录或重新选择角色会重新写入角色标记,因此重新登录后可恢复。
|
||||
|
||||
该校验采用 fail-closed(失败时拒绝)策略,目的是避免角色切换后旧 Token 继续接收不属于当前角色的消息。
|
||||
|
||||
### 3.2 前端当前会无限重试同一失败会话
|
||||
|
||||
当前 `src/composables/useAdminMessageSSE.js` 在 `EventSource.onerror` 后执行关闭和指数退避,但没有连续失败上限,也没有触发重新登录或鉴权恢复流程。
|
||||
|
||||
原生 `EventSource.onerror` 不暴露 HTTP 状态码和响应正文,前端不能仅凭 `onerror` 精确区分 401/403、服务异常和临时断网。因此不能把所有错误都直接判定为 Token 失效,但必须限制无休止重连。
|
||||
|
||||
### 3.3 默认 `message` 事件被重复注册
|
||||
|
||||
当前实现同时注册:
|
||||
|
||||
```js
|
||||
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.onmessage` 与 `es.addEventListener('message', ...)` 只保留一种。
|
||||
- [ ] `connected`、`unread-count`、`im-chat`、`im-chat-read`、`presence`、`grab-pool-changed` 等具名事件继续分别注册。
|
||||
- [ ] 验证单条默认 `message`、聊天信令和未读数信令都只被业务层消费一次。
|
||||
|
||||
### 5.4 失败信息与联调反馈
|
||||
|
||||
- [ ] 前端提示中不要展示 Token、完整 SSE URL、Cookie 或管理员标识。
|
||||
- [ ] 如重新登录后仍失败,只反馈发生时间、页面、错误 `message` 和 `X-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 实时推送](../2026-06/04_3414_内部员工站内信收件箱-SSE实时推送-管理后台.md) | SSE 路径、Query 鉴权和事件契约仍有效;其中“断线自动重连”的建议被本文补充为有上限的受控重连,鉴权持续失败时不得无限请求 |
|
||||
| [切角色 / 刷新令牌原子保存](../2026-06/38_4529_切角色与刷新token原子保存_前端必改-管理后台.md) | `token` 与 `refreshToken` 原子保存要求仍有效;保存新 access token 后还必须关闭旧 SSE 并主动重建 |
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户