# 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 数据模型 ```go // 用户实体 - 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 用户注册流程 ```go // 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 用户登录流程 ```go // 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创建 ```go // 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验证 ```go // 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 用户分组管理 ```go // 用户分组 - 用于资源隔离和配额控制 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 用户配额与限制 ```go // 用户级别限制配置 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 用户属性管理 ```go // 自定义用户属性 - 支持扩展字段 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 角色权限 ```go 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权限继承 ```go // API Key继承用户的权限和配额 type APIKeyPermission struct { UserID int64 GroupID *int64 Quota float64 // 继承用户的配额 RateLimit float64 // 继承用户的速率限制 Models []string // 继承分组的模型限制 } ``` ## 5. 数据访问层 ### 5.1 用户Repository ```go // 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 ```go // 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) ```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配置 ```yaml 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:调整用户并发限制** ```go // 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:添加用户属性** ```go // 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配额逻辑** ```go // 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*