Files
tokens-reef/deploy/docs-backup/MODULE_04_USER_APIKEY.md
Developer 349d783fd1 refactor: clean up project structure
- Remove old review reports (keep latest only)
- Move docs/ to deploy/docs-backup/
- Move performance-testing/ to deploy/
- Clean up test output files
- Organize root directory
2026-04-06 23:36:03 +08:00

16 KiB
Raw Permalink Blame History

Sub2API 模块分析报告用户与API Key管理模块

1. 模块概述

1.1 模块定位

用户与API Key管理模块是Sub2API系统的用户资源管理核心负责管理系统中所有用户账户、API Key的创建、分配、权限控制以及用户分组等操作。该模块与认证模块紧密配合共同构成系统的访问控制体系。

1.2 核心职责

  • 用户生命周期管理:用户注册、登录、信息修改、注销
  • API Key管理:创建、分配、吊销、权限控制
  • 用户分组管理:用户分组、分组权限、组内资源分配
  • 配额与限制:用户级别的配额、并发限制、速率限制
  • 用户属性管理:自定义属性、标签、扩展信息

2. 代码结构分析

2.1 核心文件

文件路径 职责 代码行数
service/user.go 用户服务核心逻辑 ~500行
service/api_key_service.go API Key服务 ~900行
service/admin_service.go 管理后台用户操作 ~2000行
handler/user_handler.go 用户相关API处理器 ~400行
handler/admin/user_handler.go 管理后台用户处理器 ~400行
handler/api_key_handler.go API Key处理器 ~300行
handler/admin/apikey_handler.go 管理后台API Key处理器 ~200行
repository/user_repo.go 用户数据访问层 ~600行
repository/api_key_repo.go API Key数据访问层 ~400行

2.2 数据模型

// 用户实体 - ent/schema/user.go
type User struct {
    ID              int64
    Email           string     // 邮箱(唯一)
    PasswordHash    string     // 密码哈希
    Name            string     // 显示名称
    Avatar          string     // 头像URL
    Status          string     // 用户状态active/disabled
    Balance         float64    // 账户余额
    Concurrency     int        // 并发数限制
    RateMultiplier  float64    // 计费倍率
    TOTPEnabled     bool       // 是否启用双因素认证
    TOTPSecret      string     // TOTP密钥加密存储
    LastLoginAt     *time.Time // 最后登录时间
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

// API Key实体 - ent/schema/apikey.go  
type APIKey struct {
    ID              int64
    Key             string     // Key值sk-开头)
    Name            string     // 名称
    UserID          int64      // 所属用户
    GroupID         *int64     // 绑定分组
    Quota           float64    // 配额0为无限制
    QuotaUsed       float64    // 已使用配额
    Status          string     // 状态active/disabled/quota_exhausted/expired
    RateLimit5h     float64    // 5小时速率限制
    RateLimit1d     float64    // 1天速率限制  
    RateLimit7d     float64    // 7天速率限制
    ExpiresAt       *time.Time // 过期时间
    IPWhitelist     string     // IP白名单JSON数组
    LastUsedAt      *time.Time // 最后使用时间
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

3. 功能详细分析

3.1 用户注册与登录

3.1.1 用户注册流程

// service/auth_service.go - Register
func (s *AuthService) Register(ctx context.Context, req RegisterRequest) (*User, error) {
    // 1. 验证邮箱格式
    if !isValidEmail(req.Email) {
        return nil, ErrInvalidEmail
    }
    
    // 2. 检查邮箱是否已存在
    if exists, _ := s.userRepo.ExistsByEmail(ctx, req.Email); exists {
        return nil, ErrEmailExists
    }
    
    // 3. 密码强度验证
    if !isStrongPassword(req.Password) {
        return nil, ErrWeakPassword
    }
    
    // 4. 密码哈希
    passwordHash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), 12)
    
    // 5. 创建用户
    user := &User{
        Email:        req.Email,
        PasswordHash: string(passwordHash),
        Name:         req.Name,
        Status:       StatusActive,
        Balance:      0,
        Concurrency: 5,  // 默认并发限制
    }
    
    return s.userRepo.Create(ctx, user)
}

3.1.2 用户登录流程

// service/auth_service.go - Login
func (s *AuthService) Login(ctx context.Context, email, password string) (*LoginResponse, error) {
    // 1. 获取用户
    user, err := s.userRepo.GetByEmail(ctx, email)
    if err != nil {
        return nil, ErrInvalidCredentials
    }
    
    // 2. 验证密码
    if !bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) {
        // 记录登录失败
        s.recordLoginFailure(ctx, user.ID)
        return nil, ErrInvalidCredentials
    }
    
    // 3. 检查用户状态
    if user.Status != StatusActive {
        return nil, ErrUserDisabled
    }
    
    // 4. 如果启用了TOTP验证TOTP码
    if user.TOTPEnabled {
        // 返回需要TOTP验证的标记
        return &LoginResponse{
            RequireTOTP: true,
            UserID:     user.ID,
        }, nil
    }
    
    // 5. 生成JWT Token
    token, err := s.generateJWT(user)
    if err != nil {
        return nil, err
    }
    
    // 6. 更新最后登录时间
    s.userRepo.UpdateLastLogin(ctx, user.ID)
    
    return &LoginResponse{
        Token: token,
        User:  user,
    }, nil
}

3.2 API Key管理

3.2.1 API Key创建

// service/api_key_service.go - Create
func (s *APIKeyService) Create(ctx context.Context, userID int64, req CreateAPIKeyRequest) (*APIKey, error) {
    // 1. 生成随机Key
    key := "sk-" + generateRandomKey(32)
    
    // 2. 检查用户API Key数量限制
    count, _ := s.apiKeyRepo.CountByUser(ctx, userID)
    if count >= maxAPIKeysPerUser {
        return nil, ErrTooManyAPIKeys
    }
    
    // 3. 创建API Key
    apiKey := &APIKey{
        Key:      key,
        Name:     req.Name,
        UserID:   userID,
        GroupID:  req.GroupID,
        Quota:    req.Quota,
        Status:   StatusActive,
        RateLimit5h: req.RateLimit5h,
        RateLimit1d: req.RateLimit1d,
        RateLimit7d: req.RateLimit7d,
    }
    
    // 4. 处理IP白名单
    if len(req.IPWhitelist) > 0 {
        apiKey.IPWhitelist = json.Marshal(req.IPWhitelist)
    }
    
    // 5. 保存到数据库
    created, err := s.apiKeyRepo.Create(ctx, apiKey)
    if err != nil {
        return nil, err
    }
    
    // 6. 返回时只显示一次Key
    return created, nil
}

3.2.2 API Key验证

// service/api_key_service.go - ValidateKey
func (s *APIKeyService) ValidateKey(ctx context.Context, key string) (*APIKey, *User, error) {
    // 1. 缓存查询
    if cached := s.getCache(key); cached != nil {
        return cached.APIKey, cached.User, nil
    }
    
    // 2. 数据库查询
    apiKey, err := s.apiKeyRepo.GetByKey(ctx, key)
    if err != nil {
        return nil, nil, ErrInvalidKey
    }
    
    // 3. 验证状态
    if apiKey.Status == StatusDisabled {
        return nil, nil, ErrKeyDisabled
    }
    if apiKey.Status == StatusQuotaExhausted {
        return nil, nil, ErrQuotaExhausted
    }
    if apiKey.Status == StatusExpired || (apiKey.ExpiresAt != nil && time.Now().After(*apiKey.ExpiresAt)) {
        return nil, nil, ErrKeyExpired
    }
    
    // 4. 验证配额
    if apiKey.Quota > 0 && apiKey.QuotaUsed >= apiKey.Quota {
        return nil, nil, ErrQuotaExhausted
    }
    
    // 5. 获取用户信息
    user, err := s.userRepo.GetByID(ctx, apiKey.UserID)
    if err != nil || user.Status != StatusActive {
        return nil, nil, ErrUserDisabled
    }
    
    // 6. 缓存结果
    s.setCache(key, apiKey, user)
    
    return apiKey, user, nil
}

3.3 用户分组管理

// 用户分组 - 用于资源隔离和配额控制
type Group struct {
    ID              int64
    Name            string
    Platform        string    // 所属平台anthropic/openai/gemini等
    Status          string    // active/disabled
    RateMultiplier  float64   // 计费倍率
    MaxConcurrency  int       // 最大并发数
    MaxSessions     int       // 最大会话数
    MaxRPM          int       // 每分钟最大请求数
    Models          []string  // 允许的模型列表
    IsExclusive     bool      // 是否独占(只能被一个用户使用)
}

3.4 用户配额与限制

// 用户级别限制配置
type UserQuota struct {
    Balance         float64   // 账户余额
    Concurrency      int       // 最大并发数
    RateMultiplier  float64   // 计费倍率默认1.0
    MonthlyQuota    float64   // 月度配额
    DailyLimit      float64   // 每日限制
}

// 使用量检查
func (s *UserService) CheckQuota(ctx context.Context, userID int64, cost float64) error {
    user, err := s.userRepo.GetByID(ctx, userID)
    if err != nil {
        return err
    }
    
    // 检查余额
    if user.Balance > 0 && user.Balance < cost {
        return ErrInsufficientBalance
    }
    
    // 检查并发限制
    activeConns := s.getActiveConnections(userID)
    if activeConns >= user.Concurrency {
        return ErrConcurrencyExceeded
    }
    
    return nil
}

3.5 用户属性管理

// 自定义用户属性 - 支持扩展字段
type UserAttribute struct {
    ID         int64
    Name       string    // 属性名
    Type       string    // 类型string/number/boolean
    Required   bool      // 是否必填
    Default    string    // 默认值
}

// 用户属性值
type UserAttributeValue struct {
    UserID     int64
    AttributeID int64
    Value      string
}

4. 权限控制

4.1 角色权限

const (
    RoleUser      = "user"       // 普通用户
    RoleAdmin     = "admin"      // 管理员
    RoleSuperAdmin = "super_admin" // 超级管理员
)

// 权限检查
func (s *UserService) CheckPermission(userID int64, action string) bool {
    user, _ := s.userRepo.GetByID(ctx, userID)
    
    switch action {
    case "user:read":
        return true
    case "user:write":
        return user.Role == RoleAdmin || user.Role == RoleSuperAdmin
    case "admin:*":
        return user.Role == RoleSuperAdmin
    default:
        return false
    }
}

4.2 API Key权限继承

// API Key继承用户的权限和配额
type APIKeyPermission struct {
    UserID      int64
    GroupID     *int64
    Quota       float64    // 继承用户的配额
    RateLimit   float64    // 继承用户的速率限制
    Models      []string   // 继承分组的模型限制
}

5. 数据访问层

5.1 用户Repository

// repository/user_repo.go
type UserRepository interface {
    Create(ctx context.Context, user *User) (*User, error)
    GetByID(ctx context.Context, id int64) (*User, error)
    GetByEmail(ctx context.Context, email string) (*User, error)
    Update(ctx context.Context, user *User) error
    Delete(ctx context.Context, id int64) error
    ExistsByEmail(ctx context.Context, email string) (bool, error)
    List(ctx context.Context, params PaginationParams, filters UserFilters) ([]User, int64, error)
    UpdateBalance(ctx context.Context, userID int64, amount float64) error
    UpdateConcurrency(ctx context.Context, userID int64, concurrency int) error
}

5.2 API Key Repository

// repository/api_key_repo.go
type APIKeyRepository interface {
    Create(ctx context.Context, key *APIKey) (*APIKey, error)
    GetByID(ctx context.Context, id int64) (*APIKey, error)
    GetByKey(ctx context.Context, key string) (*APIKey, error)
    GetByUserID(ctx context.Context, userID int64) ([]APIKey, error)
    Update(ctx context.Context, key *APIKey) error
    Delete(ctx context.Context, id int64) error
    CountByUser(ctx context.Context, userID int64) (int, error)
    List(ctx context.Context, params PaginationParams, filters APIKeyFilters) ([]APIKey, int64, error)
}

6. 配置参数

6.1 用户配置config.yaml

user:
  # 注册配置
  registration:
    enabled: true          # 允许注册
    email_verification: false  # 邮箱验证
    password_min_length: 8
    password_require_complexity: true
    
  # 登录配置
  login:
    max_attempts: 5       # 最大登录尝试
    lockout_duration: 15m # 锁定时长
    session_timeout: 24h  # 会话超时
    
  # 用户限制
  limits:
    max_api_keys_per_user: 50  # 每用户最大API Key数
    default_concurrency: 5     # 默认并发数
    default_balance: 0          # 默认余额

6.2 API Key配置

api_key:
  # 默认速率限制
  default_rate_limits:
    rate_limit_5h: 100000
    rate_limit_1d: 500000
    rate_limit_7d: 3500000
    
  # 缓存配置
  cache:
    enabled: true
    l1_size: 10000
    l1_ttl: 1m
    l2_ttl: 5m

7. 修改和扩展指南

7.1 常见修改场景

场景1调整用户并发限制

// service/user.go - UpdateConcurrency
func (s *UserService) UpdateConcurrency(ctx context.Context, userID int64, concurrency int) error {
    // 验证限制范围
    if concurrency < 1 || concurrency > 100 {
        return ErrInvalidConcurrency
    }
    
    user, _ := s.userRepo.GetByID(ctx, userID)
    user.Concurrency = concurrency
    
    return s.userRepo.Update(ctx, user)
}

场景2添加用户属性

// 1. 在 ent/schema/user.go 添加字段
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("custom_field").Optional(),
    }
}

// 2. 在 handler 中添加访问接口
router.PUT("/users/:id/custom-field", updateCustomField)

场景3修改API Key配额逻辑

// service/api_key_service.go - CheckQuota
func (s *APIKeyService) CheckQuota(ctx context.Context, key *APIKey, cost float64) error {
    // 修改配额检查逻辑
    if key.Quota > 0 {
        // 改为允许一定比例的超支
        allowedOverdraft := key.Quota * 0.1  // 10%超支额度
        if key.QuotaUsed + cost > key.Quota + allowedOverdraft {
            return ErrQuotaExhausted
        }
    }
    return nil
}

7.2 注意事项

  1. 安全性:用户密码必须使用强哈希存储
  2. 数据一致性API Key与用户关系需要级联处理
  3. 性能:用户列表查询需要分页和索引优化

8. 测试覆盖

8.1 单元测试

测试文件 覆盖范围
auth_service_register_test.go 用户注册逻辑
api_key_service_test.go API Key CRUD
user_service_test.go 用户管理逻辑

8.2 集成测试

测试文件 场景
e2e_user_flow_test.go 完整用户使用流程

9. 监控与运维

9.1 关键指标

指标 告警阈值 说明
user_register_count - 用户注册数
user_login_failures > 10/min 登录失败数
api_key_count - API Key总数
api_key_quota_exhausted > 20% 配额耗尽比例

9.2 运维任务

任务 频率 说明
清理无效用户 每月 清理长期未登录用户
检查API Key 每周 检查过期Key并通知
用户数据分析 每周 用户活跃度分析

10. 总结

用户与API Key管理模块特点

  • 完整的用户生命周期:从注册到注销的完整管理
  • 灵活的权限控制:基于角色和分组的权限体系
  • API Key安全支持IP白名单、速率限制、配额控制
  • 双因素认证支持TOTP增强安全性

潜在改进点:

  1. 激活码和API Key目前没有包含系统标识存在跨实例使用风险
  2. 用户分组权限控制可以更细粒度

修改建议:

  • 如需解决跨实例使用问题可在Key生成时嵌入实例ID
  • 权限修改需要谨慎测试,避免影响现有功能

文档版本1.0 最后更新2025-01 分析基于Sub2API v0.1.104