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

557 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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*