用户端与管理端登录有效期改为 30 天 #42

Closed
opened 2026-09-16 20:56:10 +08:00 by ila · 3 comments
Owner

来源与目标

用户 2026-09-16 提出:「用户端和管理端登录有效期改成30天」。目标是减少频繁重新登录,同时把会话有效期这一安全参数集中到一处、可测可查。

现状(已核实,非估计)

项目 实测
当前有效期 8 小时,硬编码在 server/app/lexgo/service.go:165 的 expiry := now.Add(8 * time.Hour)
适用端 学习端与管理端共用同一登录接口与同一有效期,因此一处修改同时生效
存储 lexgo_sessions(token_hash 主键、owner_id、expires_at)只保存令牌的 SHA-256 摘要,客户端持有随机令牌
校验 loginCredentials 之后按 expires_at > now 判断;登录时会顺带清理该账号已过期会话
撤销 退出只撤销当前会话;改密码、停用、重置会撤销该账号全部会话
续期 无 refresh、无滑动续期:到期即必须重新登录
需要同步更新 server/app/lexgo/integration_test.go:284(时钟前进 9 小时断言过期)、docs/03 的会话规则数字

方案

  • 有效期改为 30 天(720 小时)绝对有效期,提取为具名常量 SessionLifetime,登录时统一使用;仍然不做滑动续期,到期必须重新登录。
  • 不改数据库结构、不改接口字段:登录响应仍是 expiresAt,客户端行为不变。
  • 既有撤销规则全部保留:退出撤销当前会话,改密码/停用/重置撤销该账号全部会话,过期会话在下次登录时清理。
  • 测试改为按新口径断言:29 天仍可用、超过 30 天被拒绝(不用"9 小时"这种绑死旧值的写法)。

影响与风险(安全边界)

  • 令牌泄露后的可用窗口从 8 小时变成 30 天,放大约 90 倍。缓解手段维持现状:服务端只存摘要、令牌只经 HTTPS 正文/头部传递不落 URL、改密码与停用立即全撤销、账号之间数据仍按归属隔离。
  • 本次不新增:刷新令牌与轮换、设备/会话列表、「退出其他设备」、管理员强制下线(已有"停用账号即撤销全部会话")。如需,另开单元工单。
  • 多账号自托管场景下,共享设备上的长会话意味着他人可直接进入;这是本决策的已知代价,由用户确认。

非目标

滑动续期或"记住我"开关、JWT 迁移、按角色区分有效期(用户已明确两端都是 30 天)、会话管理界面。

验收标准

  • 登录后 30 天内的会话正常可用(含跨进程重启后仍从 MySQL 校验通过)。
  • 超过 30 天的会话被拒绝,接口返回未授权,客户端回到登录页。
  • 退出只撤销当前会话;改密码/停用/重置仍撤销该账号全部会话。
  • 无 schema 变化、无接口字段变化;既有会话在修改上线后按各自 expires_at 自然过渡。
  • 文档同步:会话规则数字、验证与排错说明。

依赖与文档影响

无前置工单。文档影响:Business-Rules-and-Glossary(会话有效期与撤销规则)、
Local-Development-and-Verification(如何验证与排错)、Architecture-and-Code-Map(会话参数位置)、Home 记录本次变化。

## 来源与目标 用户 2026-09-16 提出:「**用户端和管理端登录有效期改成30天**」。目标是减少频繁重新登录,同时把会话有效期这一安全参数集中到一处、可测可查。 ## 现状(已核实,非估计) | 项目 | 实测 | |---|---| | 当前有效期 | **8 小时**,硬编码在 `server/app/lexgo/service.go:165` 的 `expiry := now.Add(8 * time.Hour)` | | 适用端 | 学习端与管理端**共用同一登录接口**与同一有效期,因此一处修改同时生效 | | 存储 | `lexgo_sessions`(`token_hash` 主键、`owner_id`、`expires_at`)只保存令牌的 SHA-256 摘要,客户端持有随机令牌 | | 校验 | `loginCredentials` 之后按 `expires_at > now` 判断;登录时会顺带清理该账号已过期会话 | | 撤销 | 退出只撤销当前会话;改密码、停用、重置会撤销该账号全部会话 | | 续期 | 无 refresh、无滑动续期:到期即必须重新登录 | | 需要同步更新 | `server/app/lexgo/integration_test.go:284`(时钟前进 9 小时断言过期)、`docs/03` 的会话规则数字 | ## 方案 - 有效期改为 **30 天(720 小时)绝对有效期**,提取为具名常量 `SessionLifetime`,登录时统一使用;仍然**不做**滑动续期,到期必须重新登录。 - 不改数据库结构、不改接口字段:登录响应仍是 `expiresAt`,客户端行为不变。 - 既有撤销规则全部保留:退出撤销当前会话,改密码/停用/重置撤销该账号全部会话,过期会话在下次登录时清理。 - 测试改为按新口径断言:29 天仍可用、超过 30 天被拒绝(不用"9 小时"这种绑死旧值的写法)。 ## 影响与风险(安全边界) - 令牌泄露后的可用窗口从 8 小时变成 30 天,放大约 90 倍。缓解手段维持现状:服务端只存摘要、令牌只经 HTTPS 正文/头部传递不落 URL、改密码与停用立即全撤销、账号之间数据仍按归属隔离。 - 本次**不新增**:刷新令牌与轮换、设备/会话列表、「退出其他设备」、管理员强制下线(已有"停用账号即撤销全部会话")。如需,另开单元工单。 - 多账号自托管场景下,共享设备上的长会话意味着他人可直接进入;这是本决策的已知代价,由用户确认。 ## 非目标 滑动续期或"记住我"开关、JWT 迁移、按角色区分有效期(用户已明确两端都是 30 天)、会话管理界面。 ## 验收标准 - [ ] 登录后 30 天内的会话正常可用(含跨进程重启后仍从 MySQL 校验通过)。 - [ ] 超过 30 天的会话被拒绝,接口返回未授权,客户端回到登录页。 - [ ] 退出只撤销当前会话;改密码/停用/重置仍撤销该账号全部会话。 - [ ] 无 schema 变化、无接口字段变化;既有会话在修改上线后按各自 `expires_at` 自然过渡。 - [ ] 文档同步:会话规则数字、验证与排错说明。 ## 依赖与文档影响 无前置工单。文档影响:`Business-Rules-and-Glossary`(会话有效期与撤销规则)、 `Local-Development-and-Verification`(如何验证与排错)、`Architecture-and-Code-Map`(会话参数位置)、`Home` 记录本次变化。 </div>
Author
Owner

#42 实施完成,待验收(2026-09-16)

改动

  • server/app/lexgo/service.go:新增具名常量 SessionLifetime = 30 * 24 * time.Hour,登录改用
    now.Add(SessionLifetime)(原为 8 * time.Hour)。学习端与管理端共用同一登录接口,所以两端同时生效。
  • 有效期仍是绝对的:请求不会顺延,没有刷新令牌或轮换。数据库结构、接口字段、客户端行为都不变,
    已有会话按各自 expires_at 自然过渡。
  • 撤销规则不变:退出撤销当前会话;改密码、停用、重置撤销该账号全部会话;登录时清理该账号已过期会话;
    服务端只保存令牌的 SHA-256 摘要。

测试(真实执行)

范围 结果
Go 集成(LEXGO_TEST_DB_NAME=lexgo_test_issue42 python scripts/server.py test-integration) 92 项通过
新增 TestMySQLSessionLifetimeIsThirtyDays 通过:常量等于 30 天、登录响应 expiresAt 与库内 expires_at 一致(毫秒精度)、库内查不到原始令牌、新会话立即可用
TestMySQLAccountIsolationAndRevocation(改按常量断言) 通过:SessionLifetime - 1h 仍返回 200,再前进 2 小时返回 401
治理 python -m unittest discover -s tests 65 项通过

管理端与学习端未改动(有效期完全由服务端裁决),因此未新增前端测试;本机开发库 lexgo_dev 仍停在
#40 的 schema v12,而本分支基于 main(v11),所以没有在该库上做真机验证——真实链路的行为由上面的
HTTP 层集成测试覆盖,待 #40 合入后再在开发实例上复验一次。这是刻意避免跨分支污染,不是能力缺口。

安全影响(需你确认的代价)

令牌泄露后的可用窗口由 8 小时变为 30 天,放大约 90 倍。现有缓解手段全部保留(只存摘要、改密码/停用
立即全撤销、退出撤销当前会话、账号间数据隔离);未包含:刷新令牌与轮换、设备/会话列表、
"退出其他设备"、按角色区分有效期。如需这些,请让我另开单元工单。

提交与文档

  • 提交:591f816;分支 fix/42-session-lifetime-30-days;PR #43 未合并。
  • Wiki revision:Business-Rules-and-Glossary 1f89692c(会话规则数字已由 8 小时改为 30 天并注明来源)、
    Architecture-and-Code-Map 64a83458、Local-Development-and-Verification 45306a5d、Home b59bdc8b、
    Project-Profile 4ed96aac。
  • 核心镜像 docs/ 暂未导出:线上 Wiki 现在同时包含 #40 与 #42 的段落,若在此分支导出会把 #40 的内容
    混进本分支差异;将在验收(#40 合入后)执行 python dev_scripts/harness.py sync 并复核。
  • Gitea MCP 仍指向其他站点,本次继续使用目标站点 API 回退;凭据只从既有安全配置读入进程。

请验收;确认后我执行验收记录、镜像同步与复核、fast-forward 合并、关闭工单并登记父工单。

## #42 实施完成,待验收(2026-09-16) ### 改动 - `server/app/lexgo/service.go`:新增具名常量 **`SessionLifetime = 30 * 24 * time.Hour`**,登录改用 `now.Add(SessionLifetime)`(原为 `8 * time.Hour`)。学习端与管理端共用同一登录接口,所以两端同时生效。 - 有效期仍是**绝对**的:请求不会顺延,没有刷新令牌或轮换。数据库结构、接口字段、客户端行为都不变, 已有会话按各自 `expires_at` 自然过渡。 - 撤销规则不变:退出撤销当前会话;改密码、停用、重置撤销该账号全部会话;登录时清理该账号已过期会话; 服务端只保存令牌的 SHA-256 摘要。 ### 测试(真实执行) | 范围 | 结果 | |---|---| | Go 集成(`LEXGO_TEST_DB_NAME=lexgo_test_issue42 python scripts/server.py test-integration`) | **92 项通过** | | 新增 `TestMySQLSessionLifetimeIsThirtyDays` | 通过:常量等于 30 天、登录响应 `expiresAt` 与库内 `expires_at` 一致(毫秒精度)、库内查不到原始令牌、新会话立即可用 | | `TestMySQLAccountIsolationAndRevocation`(改按常量断言) | 通过:`SessionLifetime - 1h` 仍返回 200,再前进 2 小时返回 401 | | 治理 `python -m unittest discover -s tests` | 65 项通过 | 管理端与学习端未改动(有效期完全由服务端裁决),因此未新增前端测试;本机开发库 `lexgo_dev` 仍停在 #40 的 schema v12,而本分支基于 main(v11),所以**没有**在该库上做真机验证——真实链路的行为由上面的 HTTP 层集成测试覆盖,待 #40 合入后再在开发实例上复验一次。这是刻意避免跨分支污染,不是能力缺口。 ### 安全影响(需你确认的代价) 令牌泄露后的可用窗口由 8 小时变为 30 天,放大约 90 倍。现有缓解手段全部保留(只存摘要、改密码/停用 立即全撤销、退出撤销当前会话、账号间数据隔离);**未包含**:刷新令牌与轮换、设备/会话列表、 "退出其他设备"、按角色区分有效期。如需这些,请让我另开单元工单。 ### 提交与文档 - 提交:`591f816`;分支 `fix/42-session-lifetime-30-days`;**PR [#43](https://git.ilapage.cn/OPC/lexgo/pulls/43) 未合并**。 - Wiki revision:Business-Rules-and-Glossary `1f89692c`(会话规则数字已由 8 小时改为 30 天并注明来源)、 Architecture-and-Code-Map `64a83458`、Local-Development-and-Verification `45306a5d`、Home `b59bdc8b`、 Project-Profile `4ed96aac`。 - 核心镜像 `docs/` 暂未导出:线上 Wiki 现在同时包含 #40 与 #42 的段落,若在此分支导出会把 #40 的内容 混进本分支差异;将在验收(#40 合入后)执行 `python dev_scripts/harness.py sync` 并复核。 - Gitea MCP 仍指向其他站点,本次继续使用目标站点 API 回退;凭据只从既有安全配置读入进程。 请验收;确认后我执行验收记录、镜像同步与复核、fast-forward 合并、关闭工单并登记父工单。
Author
Owner

#42 状态:待验收(2026-09-16)

契约与完整证据见评论 8260;分支 fix/42-session-lifetime-30-days,PR #43 未合并,等待用户验收。

验收通过后我再执行:验收记录 → 核心镜像同步与复核(#40 合入后)→ fast-forward 合并 → 关闭工单 → 勾选父工单 #16。

## #42 状态:待验收(2026-09-16) 契约与完整证据见评论 [8260](https://git.ilapage.cn/OPC/lexgo/issues/42#issuecomment-8260);分支 fix/42-session-lifetime-30-days,PR [#43](https://git.ilapage.cn/OPC/lexgo/pulls/43) **未合并**,等待用户验收。 验收通过后我再执行:验收记录 → 核心镜像同步与复核(#40 合入后)→ fast-forward 合并 → 关闭工单 → 勾选父工单 #16。
Author
Owner

#42 验收通过(2026-09-16)

用户 2026-09-16 回复「#32 #40 验收通过、#42 验收通过」,本单验收结论如下。

  • 合入:PR #43 以 rebase-ff 合入 main,提交 4e4fb85。
  • 最终状态:学习端与管理端登录有效期 30 天(具名常量 SessionLifetime,绝对有效期、不滑动续期);
    无 schema 与接口字段变化;撤销规则不变(退出撤销当前会话,改密码/停用/重置撤销该账号全部会话)。
  • 合并后在开发实例复验(6 项全通过):新登录的有效期约 29 天 23:59:59、库内 expires_at 与响应一致、
    会话可用、schema v12、两本词典在位。
  • 测试:Go 92 项(新增 1 项锁定 30 天常量与摘要存储,过期断言改为按常量计算:30天-1h 可用、再 +2h 拒绝);
    治理 65 项。
  • Wiki revision:Business-Rules 1f89692c(会话规则数字已改)、Architecture 64a83458、
    Local-Development 45306a5d、Home e6e3b2a9、Project-Profile a482d5ba;镜像随验收提交 42945de 导出并复核一致。
  • 已知代价(用户已确认):令牌泄露窗口由 8 小时放大到 30 天(约 90 倍);设备/会话列表与「退出其他设备」
    未包含,如需另开工单。

工单关闭。

## #42 验收通过(2026-09-16) 用户 2026-09-16 回复「#32 #40 验收通过、#42 验收通过」,本单验收结论如下。 - 合入:PR [#43](https://git.ilapage.cn/OPC/lexgo/pulls/43) 以 rebase-ff 合入 main,提交 `4e4fb85`。 - 最终状态:学习端与管理端登录有效期 **30 天**(具名常量 `SessionLifetime`,绝对有效期、不滑动续期); 无 schema 与接口字段变化;撤销规则不变(退出撤销当前会话,改密码/停用/重置撤销该账号全部会话)。 - 合并后在开发实例复验(6 项全通过):新登录的有效期约 29 天 23:59:59、库内 `expires_at` 与响应一致、 会话可用、schema v12、两本词典在位。 - 测试:Go 92 项(新增 1 项锁定 30 天常量与摘要存储,过期断言改为按常量计算:`30天-1h` 可用、再 +2h 拒绝); 治理 65 项。 - Wiki revision:Business-Rules `1f89692c`(会话规则数字已改)、Architecture `64a83458`、 Local-Development `45306a5d`、Home `e6e3b2a9`、Project-Profile `a482d5ba`;镜像随验收提交 `42945de` 导出并复核一致。 - 已知代价(用户已确认):令牌泄露窗口由 8 小时放大到 30 天(约 90 倍);设备/会话列表与「退出其他设备」 未包含,如需另开工单。 工单关闭。
ila closed this issue 2026-09-16 21:58:14 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/lexgo#42