fix(会话): touchSession 续期时一并续反向索引,否则会话吊销不掉
WebSocket 巡检走的 touchSession 只 EXPIRE `session:<token>`,不碰
`user-sessions:<uid>`。而这条路径存在的理由恰恰是「只开着页面挂 WebSocket、一次
HTTP 请求都不发的人」—— 这种连接碰不到 getUserByToken 里那两条并排的 expire。
于是索引先到期、会话却被巡检一直续着。之后改密码 / 禁用账号走 revokeUserSessions
就 SMEMBERS 不到这张 token:WebSocket 那边还有 publishSessionRevoked 按 userId
兜底能断掉,但 HTTP 一侧拿着那张 cookie 照用不误 —— 而改密码要的恰恰是让 HTTP
立刻失效(学生密码是明文存着给老师查的,改密码是密码泄露后唯一的补救手段)。
签名加一个 userId,两条 EXPIRE 走一次 pipeline,仍然只有一趟往返,原来「比
GET + EXPIRE 少一趟」的理由保住了。三个调用点都有现成的 ws.data.userId。
返回值只看会话那条:反向索引是 498fc1c 才加的,在那之前签发的会话本来就没有索引
键,续不到是正常的,不能因此判定会话已死。
实跑验过四种情况:正常会话两边都续到 7 天;无索引键的存量会话仍判活;会话已删返回
false;空 token 返回 false。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvmqDsZNyUo9P3sFQtoWVB
This commit is contained in:
@@ -178,7 +178,7 @@ export async function sweepSessions() {
|
||||
let alive = checked.get(token)
|
||||
if (alive === undefined) {
|
||||
try {
|
||||
alive = await touchSession(token)
|
||||
alive = await touchSession(token, ws.data.userId)
|
||||
} catch (error) {
|
||||
// Redis 抖一下不该把全班踢下线:这一轮直接放弃,下一轮再说
|
||||
console.error("Failed to verify websocket sessions", error)
|
||||
@@ -299,7 +299,7 @@ async function handleMessage(
|
||||
|
||||
// 会话可能在连接期间就失效了:用户在别的标签页登出,或者会话自己到期。
|
||||
// 握手时校验过一次不算数 —— 这条连接能挂几个小时。
|
||||
if (!(await touchSession(ws.data.token))) {
|
||||
if (!(await touchSession(ws.data.token, ws.data.userId))) {
|
||||
ws.close(1008, "Session expired")
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user