Files
user-system/docs/sprints/SPRINT_13_COMPLETION_REPORT.md
long-agent 5b6bd93179 refactor: 整理项目根目录结构
整理内容:
- 删除 60+ 临时测试输出文件 (*.txt)
- 移动二进制文件到 bin/ 目录
- 移动 Shell 脚本到 scripts/ 目录
  - scripts/dev/: check_gitea.sh, check_sub2api.sh, run_tests.sh
  - scripts/deploy/: deploy_*.sh, simple_deploy.sh
  - scripts/ops/: fix_nginx.sh, fix_ssl.sh, install_docker.sh
  - scripts/test/: test_*.sh, test_*.bat
- 移动批处理文件到 scripts/
- 移动 Python 脚本到 tools/
- 清理临时日志文件

保留根目录必要文件:
- go.mod, go.sum, go.work
- Makefile, docker-compose.yml
- .env.example, .gitignore
- README.md, AGENTS.md, DEPLOY_GUIDE.md

验证: go build ./... && go test ./... 通过
2026-04-07 18:10:36 +08:00

5.9 KiB
Raw Permalink Blame History

Sprint 13 完成报告

执行日期: 2026-04-02
Sprint 目标: 处理 P2 设计断链问题,补齐 GAP 关键链路
状态: 全部核心任务完成


执行摘要

Sprint 13 聚焦于 PRD_GAP_DESIGN_PLAN.md 中识别的关键设计断链问题。本轮修复覆盖安全漏洞、密码历史链路完整性、设备信任链路三大方向。


任务完成情况

GAP-01: 角色继承 — 确认已完整实现(无需修改)

调研结论

  • internal/service/role.go:循环检测 checkCircularInheritance + 深度限制 checkInheritanceDepth5层
  • internal/api/middleware/auth.goloadUserRolesAndPerms 中收集祖先角色ID并汇总权限
  • 此 GAP 已关闭,无需额外修复

GAP-02: SMS 密码重置验证码时序泄漏修复

文件: internal/service/password_reset.go

问题: 短信验证码比较使用普通字符串 !=,存在时序攻击窗口

修复:

// 修复前
if !ok || code != req.Code {
    return errors.New("验证码不正确")
}

// 修复后
if !ok || subtle.ConstantTimeCompare([]byte(code), []byte(req.Code)) != 1 {
    return errors.New("验证码不正确")
}

影响: 防止通过响应时间差枚举有效验证码


密码历史记录: doResetPassword 补写历史

文件: internal/service/password_reset.go

问题: doResetPassword被邮件重置和SMS重置共同调用不检查密码历史不写入历史记录

修复:

  1. PasswordResetService 新增 passwordHistoryRepo 字段
  2. 新增 WithPasswordHistoryRepo() 链式方法(便于注入)
  3. doResetPassword 现在:
    • 检查新密码是否与最近5次密码重复
    • 重置成功后异步写入密码历史记录,并清理超限旧记录

注入点: cmd/server/main.go

passwordResetService := service.NewPasswordResetService(userRepo, cacheManager, passwordResetConfig).
    WithPasswordHistoryRepo(passwordHistoryRepo)

GAP-05: AnomalyDetector — 确认已接线(无需修改)

调研结论:

  • cmd/server/main.go 第 111-112 行已初始化并注入
anomalyDetector := security.NewAnomalyDetector(security.DefaultAnomalyConfig, ipFilter)
authService.SetAnomalyDetector(anomalyDetector)
  • 此 GAP 已关闭

GAP-03: 设备信任链路 — 补齐设备 ID 传递

问题分析 设备信任链路存在以下断点:

断点 描述
auth_handler.go::Login handler 未接收 device_id 等字段,无法传入 LoginRequest
sms_handler.go::LoginByCode 完全是 stub不调用真实 AuthService.LoginByCode
LoginByEmailCode auth_handler 中的 stub未连接 auth_email.go 的实现

修复内容:

1. internal/api/handler/auth_handler.go — 补齐密码登录设备字段

// 修复前Login 不接收 device 字段
var req struct {
    Account  string `json:"account"`
    Password string `json:"password"`
    // ❌ 缺少 DeviceID, DeviceName, DeviceBrowser, DeviceOS
}

// 修复后:完整接收设备信息
var req struct {
    Account       string `json:"account"`
    Password      string `json:"password"`
    DeviceID      string `json:"device_id"`      // ✅ 新增
    DeviceName    string `json:"device_name"`    // ✅ 新增
    DeviceBrowser string `json:"device_browser"` // ✅ 新增
    DeviceOS      string `json:"device_os"`      // ✅ 新增
}

2. internal/api/handler/sms_handler.go — 重写为真实实现

  • SMSHandler 所有方法均为 stub
  • 新增 NewSMSHandlerWithService(authService, smsCodeService) 构造函数
  • LoginByCode 现在调用 authService.LoginByCode(),并在成功后异步调用 BestEffortRegisterDevicePublic() 注册设备

3. internal/service/auth.go — 导出设备注册公共方法

// 新增公共方法,供 SMS/邮箱验证码等非密码登录路径使用
func (s *AuthService) BestEffortRegisterDevicePublic(ctx context.Context, userID int64, req *LoginRequest) {
    s.bestEffortRegisterDevice(ctx, userID, req)
}

验证结果

验证项 结果
go build ./... 通过
go vet ./... 通过
go test ./... -count=1 全部通过(含 e2e、integration、security 等)
Lint受改文件 无错误

修改的文件清单

文件 类型 修改描述
internal/service/password_reset.go 修改 添加 subtle 比较 + 密码历史检查/记录 + WithPasswordHistoryRepo
internal/api/handler/auth_handler.go 修改 Login 补齐 device 字段接收与传递
internal/api/handler/sms_handler.go 重写 从 stub 改为真实实现,支持设备注册
internal/service/auth.go 修改 导出 BestEffortRegisterDevicePublic
cmd/server/main.go 修改 注入 passwordHistoryRepo 到 passwordResetService

关闭的 GAP 项

GAP 描述 状态
GAP-01 角色继承 已实现Sprint 12 调研确认)
GAP-02 SMS 密码重置 已完整修复(时序泄漏 + 密码历史)
GAP-05 异地/设备检测 AnomalyDetector 已接线
GAP-03 设备信任链路 主路径补齐(密码登录 + SMS登录

遗留项

项目 描述 优先级
邮箱验证码登录 handler auth_handler.go::LoginByEmailCode 仍是 stub P2
device_id 稳定性 前端 device_id 仍为随机生成,需稳定化 P2
GAP-04 (CAS/SAML SSO) 明确推迟至 v2.0 P3
GAP-07 (SDK) 明确推迟至 v2.0 P3

下一步建议

  1. Sprint 14: 补齐邮箱验证码登录真实 handler + 前端 device_id 稳定化方案
  2. Sprint 14: 清理 SlidingWindowLimiter 死代码R6-02 建议项)
  3. 前端联调: 在密码登录接口中传递真实的 device_id(可用 fingerprint.js 生成稳定值)