topfans/docs/账号状态实时响应技术方案.md
2026-04-27 13:17:57 +08:00

163 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 账号状态实时响应技术方案
## 背景需求
当管理员对用户账号进行冻结/封禁操作时,需要立即将当前在线用户的登录状态强制退出,保障平台安全。
---
## 方案一WebSocket 实时推送
### 核心原理
服务端与客户端建立 TCP 长连接,当账号状态发生变化时,服务端主动通过 WebSocket 通道推送踢人指令,客户端收到后立即跳转登录页。
### 核心技术点
| 技术点 | 说明 |
|--------|------|
| **连接管理** | 服务端维护所有在线用户的 WebSocket 连接,使用 Map<userID, Connection> 管理 |
| **心跳机制** | 客户端每 30s 发送心跳,服务端检测存活,断线时移除连接 |
| **断线重连** | 客户端检测到断连后采用指数退避策略重连1s、2s、4s...最大 30s |
| **消息协议** | 推送消息格式 `{type: "KICK_OUT", reason: "账号已被封禁", timestamp: 123456789}` |
| **多端同步** | 一个用户可能在手机、平板多端登录,需同时向所有连接推送 |
### 流程图
```
管理员操作 → 更新账号状态 → 消息队列 → WebSocket服务 → 推送至所有在线连接 → 客户端跳转登录页
```
### 优缺点
- ✅ 实时性最强(毫秒级)
- ✅ 支持主动推送,可扩展系统通知等场景
- ❌ 需要维护长连接池,增加运维复杂度
- ❌ 移动端电量消耗较高
- ❌ 弱网络环境下可能延迟不稳定
---
## 方案二:轮询检测
### 核心原理
客户端定期(如每 60 秒)调用账号状态查询接口,服务端返回当前账号状态,客户端根据返回值决定是否踢出用户。
### 核心技术点
| 技术点 | 说明 |
|--------|------|
| **轮询间隔** | 建议 60 秒,平衡实时性与服务器压力 |
| **请求优化** | 使用 `Last-Modified` / `ETag` 协商缓存,无变化时服务端返回 304 |
| **静默检测** | 检测在后台静默进行,不阻塞用户操作,不打断交互 |
| **差异化策略** | 新用户/高风险用户缩短轮询间隔,老用户/低风险用户延长间隔 |
| **错误处理** | 网络异常时继续轮询,不因单次失败终止 |
### 流程图
```
客户端定时器(60s) → 查询账号状态API → 返回status →
├─ status == "normal" → 继续使用
└─ status == "frozen/banned" → 跳转登录页 + 显示原因
```
### 优缺点
- ✅ 实现简单,无需额外基础设施
- ✅ 可复用现有接口,改动小
- ✅ 调试方便,日志清晰
- ❌ 存在延迟,最多等待一个轮询周期
- ❌ 大规模用户时请求量较高
- ❌ 客户端电量消耗
---
## 方案三JWT Token 黑名单
### 核心原理
当账号被封禁时,将该用户的 Token 加入黑名单,后续每次请求网关校验 Token 时检查黑名单,发现则返回 401 强制登出。
### 核心技术点
| 技术点 | 说明 |
|--------|------|
| **存储选型** | 使用 Redis Set 存储黑名单 TokenO(1) 查询效率 |
| **Key 设计** | `blacklist:{userID}``blacklist:{tokenSignature}` |
| **过期策略** | Token 本身有过期时间,黑名单设置相同 TTL自动清理 |
| **网关校验** | 在 JWT 校验层增加黑名单检查逻辑,位于 Token 验证之后 |
| **性能优化** | 本地缓存热点用户黑名单,减少 Redis 请求 |
| **批量封禁** | 管理员操作后可批量写入黑名单,减少数据库操作 |
### 流程图
```
管理员封禁 → 写入Redis黑名单 → 用户请求 →
→ 网关校验JWT → 检查黑名单 →
├─ 不在黑名单 → 继续处理请求
└─ 在黑名单 → 返回401 + 跳转登录页
```
### 优缺点
- ✅ 无需客户端配合,服务端完全可控
- ✅ 分布式友好,多实例共享黑名单
- ✅ 安全性高Token 失效立即生效
- ❌ 无法主动通知用户"为什么被封"
- ❌ 需要引入 Redis 依赖
---
## 推荐方案:轮询 + JWT 黑名单组合
### 组合策略
| 场景 | 处理方式 | 实时性 |
|------|---------|--------|
| **登录时** | 检测账号状态,异常拒绝登录 | 即时 |
| **登录后静默检测** | 每 60 秒轮询账号状态 | ≤60秒 |
| **被封禁后** | Token 加入黑名单,下次请求时拦截 | 即时 |
| **Token 校验** | 网关层检查黑名单 | 即时 |
### 为什么组合使用
1. **轮询**解决"不知道为什么被封"的问题,用户下次操作时能拿到具体原因
2. **黑名单**解决"Token 仍然有效"的问题,被封用户即使不轮询,请求也会被拦截
3. 两者互补,覆盖所有边界场景
### 延迟分析
| 操作 | 响应时间 |
|------|---------|
| 管理员封禁 → 请求被拦截 | < 1秒下次请求 |
| 管理员封禁 前端感知 | < 60秒轮询周期 |
### 后续扩展
如平台后续需要更多实时通知如系统公告消息提醒可在组合方案基础上引入 WebSocket不会浪费已有投资
---
## 附录:数据库表结构参考
```sql
-- 账号状态表
CREATE TABLE user_account_status (
id SERIAL PRIMARY KEY,
user_id BIGINT NOT NULL UNIQUE,
status VARCHAR(20) NOT NULL, -- normal/frozen/banned
reason TEXT, -- 冻结/封号原因
frozen_until BIGINT, -- 冻结解封时间戳(毫秒)
operator_id BIGINT, -- 操作人ID
operated_at BIGINT, -- 操作时间
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL
);
-- Redis 黑名单Key设计
-- Key: blacklist:user:{userID}
-- Value: 1
-- TTL: 与Token过期时间一致
```