5.5 KiB
5.5 KiB
账号状态实时响应技术方案
背景需求
当管理员对用户账号进行冻结/封禁操作时,需要立即将当前在线用户的登录状态强制退出,保障平台安全。
方案一: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 存储黑名单 Token,O(1) 查询效率 |
| Key 设计 | blacklist:{userID} 或 blacklist:{tokenSignature} |
| 过期策略 | Token 本身有过期时间,黑名单设置相同 TTL,自动清理 |
| 网关校验 | 在 JWT 校验层增加黑名单检查逻辑,位于 Token 验证之后 |
| 性能优化 | 本地缓存热点用户黑名单,减少 Redis 请求 |
| 批量封禁 | 管理员操作后可批量写入黑名单,减少数据库操作 |
流程图
管理员封禁 → 写入Redis黑名单 → 用户请求 →
→ 网关校验JWT → 检查黑名单 →
├─ 不在黑名单 → 继续处理请求
└─ 在黑名单 → 返回401 + 跳转登录页
优缺点
- ✅ 无需客户端配合,服务端完全可控
- ✅ 分布式友好,多实例共享黑名单
- ✅ 安全性高,Token 失效立即生效
- ❌ 无法主动通知用户"为什么被封"
- ❌ 需要引入 Redis 依赖
推荐方案:轮询 + JWT 黑名单组合
组合策略
| 场景 | 处理方式 | 实时性 |
|---|---|---|
| 登录时 | 检测账号状态,异常拒绝登录 | 即时 |
| 登录后静默检测 | 每 60 秒轮询账号状态 | ≤60秒 |
| 被封禁后 | Token 加入黑名单,下次请求时拦截 | 即时 |
| Token 校验 | 网关层检查黑名单 | 即时 |
为什么组合使用
- 轮询解决"不知道为什么被封"的问题,用户下次操作时能拿到具体原因
- 黑名单解决"Token 仍然有效"的问题,被封用户即使不轮询,请求也会被拦截
- 两者互补,覆盖所有边界场景
延迟分析
| 操作 | 响应时间 |
|---|---|
| 管理员封禁 → 请求被拦截 | < 1秒(下次请求) |
| 管理员封禁 → 前端感知 | < 60秒(轮询周期) |
后续扩展
如平台后续需要更多实时通知(如系统公告、消息提醒),可在组合方案基础上引入 WebSocket,不会浪费已有投资。
附录:数据库表结构参考
-- 账号状态表
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过期时间一致