auth.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Package auth 实现认证与授权。
  2. //
  3. // 依据 docs/backend/auth.md:
  4. // - 凭证生成与哈希(token.go)
  5. // - 身份识别与授权中间件
  6. // - 会话、待验证登录、授信设备、API Token 的存取
  7. //
  8. // 约定:*gin.Context 只出现在中间件与 handler 层;
  9. // 业务函数只接受标准 context.Context(docs/architecture.md 5.2)。
  10. package auth
  11. import (
  12. "context"
  13. "errors"
  14. "time"
  15. )
  16. // 与 database/sql 交互时统一的错误。
  17. var (
  18. // ErrNotFound 表示记录不存在(或已过期、已撤销),调用方按 404 / 401 处理。
  19. ErrNotFound = errors.New("auth: 记录不存在")
  20. )
  21. // Identity 是授权中间件识别出的请求身份(auth.md 6.1)。
  22. //
  23. // 三个维度:身份种类、Token 的 scope、会话是否通过过 TOTP。
  24. type Identity struct {
  25. Kind IdentityKind
  26. UserID string
  27. // Scope 仅对 TokenUser 有意义,取值 read / read_write。
  28. Scope string
  29. // TotpVerified 仅对 SessionUser 有意义:会话创建时是否通过过 TOTP(auth.md 6.2)。
  30. TotpVerified bool
  31. }
  32. // IdentityKind 对应 auth.md 6.1 的三种身份。
  33. type IdentityKind int
  34. const (
  35. // Anonymous 未登录。零值,便于在 context 缺失时安全降级。
  36. Anonymous IdentityKind = iota
  37. // SessionUser 通过会话 cookie 登录的用户。
  38. SessionUser
  39. // TokenUser 通过 Authorization: Bearer 鉴权的 API Token 用户。
  40. TokenUser
  41. )
  42. func (k IdentityKind) String() string {
  43. switch k {
  44. case SessionUser:
  45. return "session"
  46. case TokenUser:
  47. return "token"
  48. default:
  49. return "anonymous"
  50. }
  51. }
  52. // IsLoggedIn 表示该身份是否已知(会话或 Token)。
  53. func (i Identity) IsLoggedIn() bool {
  54. return i.Kind == SessionUser || i.Kind == TokenUser
  55. }
  56. // String 返回身份种类的可读名称,用于日志与 /api/me 的 auth_type。
  57. func (i Identity) String() string {
  58. return i.Kind.String()
  59. }
  60. // Session 是 sessions 表的一行(auth.md 第三节)。
  61. type Session struct {
  62. ID string
  63. UserID string
  64. TokenHash string
  65. ExpiresAt time.Time
  66. LastUsedAt time.Time
  67. UserAgent string
  68. CreatedAt time.Time
  69. // TotpVerified 记录会话创建时是否通过过 TOTP。
  70. // auth.md 6.2 要求「仅 SessionUser 且会话创建时通过过 TOTP」才满足 totp_verified。
  71. TotpVerified bool
  72. }
  73. // TrustedDevice 是 trusted_devices 表的一行(auth.md 第四节)。
  74. type TrustedDevice struct {
  75. ID string
  76. UserID string
  77. TokenHash string
  78. DeviceName string
  79. ExpiresAt time.Time
  80. LastUsedAt time.Time
  81. CreatedAt time.Time
  82. }
  83. // APIToken 是 tokens 表的一行(auth.md 第五节)。
  84. type APIToken struct {
  85. ID string
  86. UserID string
  87. Name string
  88. TokenHash string
  89. TokenPrefix string
  90. Scope string
  91. ExpiresAt *time.Time
  92. LastUsedAt *time.Time
  93. RevokedAt *time.Time
  94. CreatedAt time.Time
  95. }
  96. // Store 是鉴权相关的数据访问接口。
  97. //
  98. // 实现放在本包的 sqlstore.go,手写 SQL(architecture.md 5.1)。
  99. // 单独抽接口是为了让中间件与业务逻辑可以在不连库的情况下测试。
  100. type Store interface {
  101. // FindSessionByHash 查未过期的会话;不存在或已过期返回 ErrNotFound。
  102. FindSessionByHash(ctx context.Context, tokenHash string) (Session, error)
  103. // TouchSession 更新 last_used_at,不延长 expires_at(auth.md 第三节规则 5)。
  104. TouchSession(ctx context.Context, id string, at time.Time) error
  105. // DeleteSession 撤销会话:硬删除(auth.md 第三节规则 7)。
  106. DeleteSession(ctx context.Context, id string) error
  107. // FindTokenByHash 查已撤销 / 过期时间也一并返回,由调用方判定。
  108. FindTokenByHash(ctx context.Context, tokenHash string) (APIToken, error)
  109. // TouchToken 更新 tokens.last_used_at。
  110. TouchToken(ctx context.Context, id string, at time.Time) error
  111. // GetUser 按 id 取用户;不存在返回 ErrNotFound。
  112. GetUser(ctx context.Context, id string) (User, error)
  113. }
苏ICP备2026082406号
苏公网安备 待审核