签入版本

This commit is contained in:
小蚂蚁云
2026-10-20 21:19:44 +08:00
commit 7e70ed6942
2943 changed files with 498573 additions and 0 deletions
@@ -0,0 +1,82 @@
# 6.16 在线用户监控
:::tip 概述
在线用户是无表模块(数据源 Redis):登录/刷新登记会话,分页查看在线列表,强退踢出(access+refresh 双令牌拉黑,防被踢会话经刷新复活)。路由前缀 /api/onlineUser。
:::
## 数据结构(Redis)
```text
zset easygoadmin:online:jti:set score = 访问令牌过期时间戳
键 easygoadmin:online:info:{jti} JSON 详情(TTL = access 剩余期 + 60s)
onlineInfo 字段:
jti / userId / username / realname / ip / location / os / browser /
loginTime / issuedAt / expireAt / refreshJti / refreshIssuedAt / refreshExpireAt /
accessToken / refreshToken(令牌原文,仅详情查看返回)/ tokenType / expiresIn
```
## 会话生命周期
```text
登录/refresh 成功
│ MarkOnline
▼
┌─────────────────────────────────────────────┐
│ zset easygoadmin:online:jti:set │ score=access过期ts
│ 键 easygoadmin:online:info:{jti} = JSON │ TTL=access剩余+60s
└──────────────────┬──────────────────────────┘
页面访问(有效期内)│ │登出 Logout / 强退 ForceLogout
(token 有效,│ ▼ MarkOffline + access/refresh 双拉黑
JWT 放行) │ zset 移除 + info 删除 → 会话消失
▼
access 到期:zset score < now 被区间过滤(列表不可见);
info 键 TTL 到期自动清除
refresh 到期:无法再换新令牌
无心跳/续期机制:会话有效期即 access 令牌有效期(2h);
前端 refresh 轮换会刷新在线登记(MarkOnline 覆盖写)
```
## 接口清单
| 方法 | 路由 | 权限 | 说明 |
|------|------|------|------|
| GET | /api/onlineUser/page | sys:onlineUser:page | 在线会话分页 |
| GET | /api/onlineUser/detail/:id | sys:onlineUser:detail | 会话详情(含令牌原文) |
| GET | /api/onlineUser/forceLogout/:id | sys:onlineUser:forceLogout | 单会话强退 |
| GET | /api/onlineUser/forceLogoutUser/:userId | sys:onlineUser:forceLogout | 踢出该用户全部设备 |
## Page 流程
```text
ZRangeByScore(now..+∞) → 逐 jti 读详情 → username/ip 子串过滤
→ 按登录时间倒序 → 同用户多会话标注(SessionNo/Sessions 序号与总数)
→ 批量回填头像(按 userId 查 user.avatar,GetFileUrl 补域名,单用户只查一次)
→ 内存分页(NewPageData)
```
## 强退的双令牌拉黑(安全关键)
```text
单会话强退(forceLogout/:id):
1) access jti 拉黑(TTL=剩余期+60s)
2) refresh jti 一并拉黑——防被踢会话经 refresh 换新令牌复活
3) MarkOffline(zset 移除 + info 删除)
踢全部设备(forceLogoutUser/:userId):
遍历 zset 匹配 userId → 逐会话双令牌拉黑 + 注销
```
::: tip 为什么双拉黑
只拉黑 access 的话,被踢用户的 refresh_token 仍能兑换新令牌对重新"复活"。access+refresh 一并拉黑才能真正踢出。
:::
## 排障
| 症状 | 原因 |
|------|------|
| 在线列表为空但有人在线 | Redis 内存降级(各实例独立);或会话已过 access 有效期 |
| 强退后对方仍能请求 | 该实例内存降级(黑名单不跨实例);等窗口或查 /api/cache/info |
| 会话数偏多 | access 2h 有效期内多次登录=多会话(同用户多设备标注序号) |
| 详情泄露令牌原文 | 设计如此(运维排查用),依赖 sys:onlineUser:detail 权限收口 |