- 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
521 lines
13 KiB
Markdown
521 lines
13 KiB
Markdown
# Sub2API 模块分析报告:认证与授权模块
|
||
|
||
## 1. 模块概述
|
||
|
||
### 1.1 模块定位
|
||
认证与授权模块是 Sub2API 的安全核心,负责验证用户身份、管理 API Key、配置访问权限。该模块确保只有授权用户才能访问系统资源,并实施精细的访问控制策略。
|
||
|
||
### 1.2 核心职责
|
||
- **用户认证**:支持密码登录、OAuth 第三方登录、JWT Token 认证
|
||
- **API Key 管理**:创建、验证、吊销 API Key
|
||
- **权限控制**:基于角色和分组的访问控制
|
||
- **二次验证**:TOTP 双因素认证支持
|
||
|
||
## 2. 代码结构分析
|
||
|
||
### 2.1 核心文件
|
||
|
||
| 文件路径 | 职责 | 代码行数 |
|
||
|---------|------|----------|
|
||
| `middleware/jwt_auth.go` | JWT Token 认证中间件 | ~300 行 |
|
||
| `middleware/api_key_auth.go` | API Key 认证中间件 | ~250 行 |
|
||
| `middleware/admin_auth.go` | 管理员权限验证 | ~150 行 |
|
||
| `service/auth_service.go` | 认证核心服务 | ~900 行 |
|
||
| `service/api_key_service.go` | API Key 服务 | ~900 行 |
|
||
| `service/totp_service.go` | TOTP 双因素认证 | ~400 行 |
|
||
| `handler/auth_handler.go` | 认证 API 处理器 | ~300 行 |
|
||
| `handler/api_key_handler.go` | API Key API 处理器 | ~300 行 |
|
||
|
||
### 2.2 目录结构
|
||
|
||
```
|
||
backend/internal/
|
||
├── server/middleware/
|
||
│ ├── jwt_auth.go # JWT 认证
|
||
│ ├── api_key_auth.go # API Key 认证
|
||
│ ├── admin_auth.go # 管理员认证
|
||
│ └── auth_subject.go # 认证主体解析
|
||
└── service/
|
||
├── auth_service.go # 认证服务
|
||
├── api_key_service.go # API Key 服务
|
||
└── totp_service.go # TOTP 服务
|
||
```
|
||
|
||
## 3. 功能详细分析
|
||
|
||
### 3.1 认证机制
|
||
|
||
#### 3.1.1 JWT Token 认证
|
||
|
||
**流程:**
|
||
```
|
||
用户登录 → 验证密码 → 生成 JWT Token → 返回给客户端
|
||
后续请求 → 验证 JWT → 解析用户信息 → 授权访问
|
||
```
|
||
|
||
**实现代码:** (`middleware/jwt_auth.go`)
|
||
```go
|
||
func NewJWTAuthMiddleware(cfg *config.Config) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
// 1. 从 Header 获取 Token
|
||
token := extractToken(c)
|
||
|
||
// 2. 解析 Token
|
||
claims, err := jwtUtils.ParseToken(token, cfg.JWT.Secret)
|
||
if err != nil {
|
||
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
|
||
return
|
||
}
|
||
|
||
// 3. 设置用户上下文
|
||
c.Set("user_id", claims.UserID)
|
||
c.Set("role", claims.Role)
|
||
c.Next()
|
||
}
|
||
}
|
||
```
|
||
|
||
**配置参数:**
|
||
- Token 有效期:24 小时(可配置)
|
||
- 签名算法:HS256/HS384/HS512
|
||
- 刷新机制:支持 Refresh Token
|
||
|
||
#### 3.1.2 API Key 认证
|
||
|
||
**特点:**
|
||
- 永久有效(除非被吊销)
|
||
- 绑定用户和分组
|
||
- 支持 IP 白名单
|
||
- 支持速率限制
|
||
|
||
**验证流程:**
|
||
```go
|
||
// service/api_key_service.go - ValidateKey
|
||
func (s *APIKeyService) ValidateKey(ctx context.Context, key string) (*APIKey, *User, error) {
|
||
// 1. 缓存查询(两级:L1 内存 + L2 Redis)
|
||
if cached := s.getAuthCache(key); cached != nil {
|
||
return cached.APIKey, cached.User, nil
|
||
}
|
||
|
||
// 2. 数据库查询
|
||
apiKey, err := s.apiKeyRepo.GetByKey(ctx, key)
|
||
if err != nil {
|
||
return nil, nil, ErrInvalidAPIKey
|
||
}
|
||
|
||
// 3. 状态检查
|
||
if apiKey.Status != StatusActive {
|
||
return nil, nil, ErrAPIKeyDisabled
|
||
}
|
||
|
||
// 4. IP 白名单检查
|
||
if !s.checkIPWhitelist(c, apiKey) {
|
||
return nil, nil, ErrIPNotAllowed
|
||
}
|
||
|
||
return apiKey, user, nil
|
||
}
|
||
```
|
||
|
||
#### 3.1.3 OAuth 第三方登录
|
||
|
||
**支持的提供商:**
|
||
- **Anthropic OAuth**:Claude 账号授权
|
||
- **Google OAuth**:Gemini 账号授权
|
||
- **OpenAI OAuth**:OpenAI 账号授权
|
||
- **Linux.do OAuth**:社区账号登录
|
||
|
||
**实现结构:**
|
||
```go
|
||
// service/oauth_service.go
|
||
type OAuthProvider interface {
|
||
GetAuthURL() string
|
||
ExchangeCode(token string) (*OAuthToken, error)
|
||
RefreshToken(token string) (*OAuthToken, error)
|
||
}
|
||
```
|
||
|
||
### 3.2 授权控制
|
||
|
||
#### 3.2.1 基于角色的访问控制 (RBAC)
|
||
|
||
**角色层级:**
|
||
```go
|
||
const (
|
||
RoleUser Role = "user" // 普通用户
|
||
RoleAdmin Role = "admin" // 管理员
|
||
RoleSuperAdmin Role = "super_admin" // 超级管理员
|
||
)
|
||
```
|
||
|
||
**权限检查:**
|
||
```go
|
||
// middleware/admin_only.go
|
||
func AdminOnly() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
role, exists := c.Get("role")
|
||
if !exists || role != RoleAdmin {
|
||
c.AbortWithStatusJSON(403, gin.H{"error": "admin access required"})
|
||
return
|
||
}
|
||
c.Next()
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3.2.2 分组级别的访问控制
|
||
|
||
```go
|
||
// API Key 绑定到分组
|
||
type APIKey struct {
|
||
GroupID *int64 // 所属分组
|
||
// 用户只能访问自己分组内的资源
|
||
}
|
||
```
|
||
|
||
### 3.3 API Key 管理
|
||
|
||
#### 3.3.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. 设置默认值
|
||
apiKey := &APIKey{
|
||
Key: key,
|
||
UserID: userID,
|
||
Status: StatusActive,
|
||
Quota: req.Quota,
|
||
// IP 白名单、速率限制等
|
||
}
|
||
|
||
// 3. 存储到数据库
|
||
return s.apiKeyRepo.Create(ctx, apiKey)
|
||
}
|
||
```
|
||
|
||
**Key 格式:** `sk-{随机32位字符}`
|
||
|
||
#### 3.3.2 缓存策略
|
||
|
||
**两级缓存架构:**
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ L1 Cache (内存) │
|
||
│ - 容量:10000 条 │
|
||
│ - TTL:1 分钟 │
|
||
│ - 命中率:~90% │
|
||
└─────────────────────────────────────┘
|
||
↑
|
||
│ 缓存未命中
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ L2 Cache (Redis) │
|
||
│ - 容量:无限制 │
|
||
│ - TTL:5 分钟 │
|
||
└─────────────────────────────────────┘
|
||
↑
|
||
│ 缓存未命中
|
||
▼
|
||
┌─────────────────────────────────────┐
|
||
│ Database (PostgreSQL) │
|
||
│ - 查询延迟:~10ms │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.4 双因素认证 (TOTP)
|
||
|
||
#### 3.4.1 启用流程
|
||
|
||
```go
|
||
// service/totp_service.go - EnableTOTP
|
||
func (s *TOTPService) EnableTOTP(ctx context.Context, userID int64) (*TOTPSetup, error) {
|
||
// 1. 生成密钥
|
||
secret := generateTOTPSecret()
|
||
|
||
// 2. 生成 QR 码(用户扫描到 authenticator app)
|
||
qrCode := generateQRCode(secret, user.Email)
|
||
|
||
// 3. 临时存储密钥(未验证前不生效)
|
||
s.tempStore.Set(userID, secret)
|
||
|
||
return &TOTPSetup{
|
||
Secret: secret,
|
||
QRCode: qrCode,
|
||
}, nil
|
||
}
|
||
```
|
||
|
||
#### 3.4.2 验证流程
|
||
|
||
```go
|
||
// service/totp_service.go - VerifyTOTP
|
||
func (s *TOTPService) VerifyTOTP(ctx context.Context, userID int64, code string) error {
|
||
// 1. 获取用户密钥
|
||
secret := s.getUserSecret(userID)
|
||
|
||
// 2. 验证 TOTP 码(支持 30s 窗口)
|
||
valid := totp.Validate(code, secret)
|
||
if !valid {
|
||
return ErrInvalidTOTP
|
||
}
|
||
|
||
// 3. 标记为已启用
|
||
s.markAsEnabled(userID)
|
||
return nil
|
||
}
|
||
```
|
||
|
||
**安全特性:**
|
||
- 密钥加密存储(AES-256)
|
||
- 支持时间窗口(前一个、后一个码可用)
|
||
- 错误锁定(连续 5 次错误锁定 30 分钟)
|
||
|
||
## 4. 安全加固
|
||
|
||
### 4.1 密码安全
|
||
|
||
```go
|
||
// service/auth_service.go
|
||
func (s *AuthService) HashPassword(password string) string {
|
||
// 使用 bcrypt,cost = 12
|
||
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 12)
|
||
return string(hash)
|
||
}
|
||
|
||
func (s *AuthService) VerifyPassword(password, hash string) bool {
|
||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||
return err == nil
|
||
}
|
||
```
|
||
|
||
**配置参数:**
|
||
- Bcrypt cost:12(可配置 10-15)
|
||
- 密码最小长度:8 字符
|
||
- 密码复杂度:无强制要求(建议启用)
|
||
|
||
### 4.2 速率限制
|
||
|
||
```go
|
||
// middleware/rate_limiter.go
|
||
func NewRateLimiter() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
key := getRateLimitKey(c)
|
||
|
||
// 基于 Token Bucket 算法
|
||
if !allowRequest(key) {
|
||
c.AbortWithStatusJSON(429, gin.H{
|
||
"error": "rate limit exceeded",
|
||
"retry_after": getRetryAfter(key),
|
||
})
|
||
return
|
||
}
|
||
c.Next()
|
||
}
|
||
}
|
||
```
|
||
|
||
**限制级别:**
|
||
| 级别 | 限制 | 说明 |
|
||
|------|------|------|
|
||
| 用户 | 1000 RPM | 每用户请求速率 |
|
||
| API Key | 500 RPM | 每 Key 请求速率 |
|
||
| IP | 2000 RPM | 每 IP 请求速率 |
|
||
|
||
### 4.3 审计日志
|
||
|
||
```go
|
||
// 记录所有认证事件
|
||
func logAuthEvent(event AuthEvent) {
|
||
logger.Info("auth_event",
|
||
"user_id", event.UserID,
|
||
"action", event.Action,
|
||
"ip", event.IP,
|
||
"success", event.Success,
|
||
"timestamp", event.Timestamp,
|
||
)
|
||
}
|
||
|
||
// 事件类型:
|
||
// - login_success / login_failed
|
||
// - logout
|
||
// - api_key_created / api_key_revoked
|
||
// - totp_enabled / totp_disabled
|
||
// - password_changed
|
||
```
|
||
|
||
## 5. 配置参数
|
||
|
||
### 5.1 认证配置(config.yaml)
|
||
|
||
```yaml
|
||
jwt:
|
||
secret: "your-256-bit-secret"
|
||
expire_hour: 24
|
||
refresh_expire_hour: 168 # 7 天
|
||
|
||
api_key:
|
||
# API Key 缓存配置
|
||
cache:
|
||
l1_size: 10000
|
||
l1_ttl: 1m
|
||
l2_ttl: 5m
|
||
|
||
rate_limit:
|
||
user_rpm: 1000
|
||
apikey_rpm: 500
|
||
ip_rpm: 2000
|
||
|
||
totp:
|
||
issuer: "Sub2API"
|
||
enabled: true
|
||
```
|
||
|
||
### 5.2 环境变量
|
||
|
||
| 变量 | 说明 | 默认值 |
|
||
|------|------|--------|
|
||
| `JWT_SECRET` | JWT 签名密钥 | 必需 |
|
||
| `TOTP_ENCRYPTION_KEY` | TOTP 密钥加密密钥 | 必需 |
|
||
| `RATE_LIMIT_ENABLED` | 启用速率限制 | true |
|
||
| `TOTP_ENABLED` | 启用 TOTP | true |
|
||
|
||
## 6. 集成与扩展
|
||
|
||
### 6.1 外部系统集成
|
||
|
||
**管理员 API Key:**
|
||
```go
|
||
// 用于外部系统(如支付系统)调用 Admin API
|
||
type AdminAPIKey struct {
|
||
Key: string // 格式:admin-<64位hex>
|
||
Role: string // admin 或 super_admin
|
||
}
|
||
```
|
||
|
||
**使用方式:**
|
||
```bash
|
||
curl -H "x-api-key: admin-xxxxxxxxxxxxxx" \
|
||
https://your-domain/api/v1/admin/users
|
||
```
|
||
|
||
### 6.2 自定义认证提供者
|
||
|
||
要添加新的认证方式,需要:
|
||
|
||
1. **实现认证接口**
|
||
```go
|
||
type AuthProvider interface {
|
||
Authenticate(credentials) (*User, error)
|
||
Refresh(token) (*User, error)
|
||
}
|
||
```
|
||
|
||
2. **注册到路由器**
|
||
```go
|
||
// server/routes/auth.go
|
||
router.POST("/auth/custom", customAuthHandler)
|
||
```
|
||
|
||
## 7. 修改和扩展指南
|
||
|
||
### 7.1 常见修改场景
|
||
|
||
**场景 1:调整 API Key 缓存策略**
|
||
|
||
修改文件:`service/api_key_auth_cache_impl.go`
|
||
```go
|
||
func (s *APIKeyService) getAuthCache(key string) (*APIKeyAuthCacheEntry, bool) {
|
||
// 调整 L1 缓存大小
|
||
l1Size := 20000 // 从 10000 增加到 20000
|
||
|
||
// 调整 L1 TTL
|
||
l1TTL := 2 * time.Minute // 从 1 分钟增加到 2 分钟
|
||
}
|
||
```
|
||
|
||
**场景 2:添加新的 OAuth 提供商**
|
||
|
||
1. 在 `service/oauth/` 目录创建新服务
|
||
2. 实现 `OAuthProvider` 接口
|
||
3. 在 `auth_service.go` 注册
|
||
|
||
**场景 3:调整速率限制**
|
||
|
||
修改文件:`middleware/rate_limiter.go`
|
||
```go
|
||
const (
|
||
UserRPM = 2000 // 从 1000 增加到 2000
|
||
APIKeyRPM = 1000 // 从 500 增加到 1000
|
||
)
|
||
```
|
||
|
||
### 7.2 安全注意事项
|
||
|
||
1. **JWT Secret 必须足够复杂**:至少 256 位随机字符串
|
||
2. **TOTP 密钥必须加密存储**:使用独立的加密密钥
|
||
3. **API Key 不得明文日志**:日志中应脱敏处理
|
||
4. **密码哈希不得使用弱算法**:禁止 MD5/SHA1
|
||
|
||
## 8. 测试覆盖
|
||
|
||
### 8.1 单元测试
|
||
|
||
| 测试文件 | 覆盖范围 |
|
||
|----------|----------|
|
||
| `jwt_auth_test.go` | JWT 解析和验证 |
|
||
| `api_key_auth_test.go` | API Key 认证流程 |
|
||
| `api_key_service_cache_test.go` | 缓存机制 |
|
||
| `totp_service_test.go` | TOTP 验证 |
|
||
|
||
### 8.2 集成测试
|
||
|
||
| 测试文件 | 场景 |
|
||
|----------|------|
|
||
| `e2e_user_flow_test.go` | 完整用户认证流程 |
|
||
| `auth_rate_limit_integration_test.go` | 速率限制验证 |
|
||
|
||
## 9. 监控与运维
|
||
|
||
### 9.1 关键指标
|
||
|
||
| 指标 | 告警阈值 | 说明 |
|
||
|------|----------|------|
|
||
| `auth_login_failures` | > 10/min | 登录失败过多 |
|
||
| `auth_api_key_validations` | - | API Key 验证次数 |
|
||
| `auth_cache_hit_rate` | < 80% | 缓存命中率低 |
|
||
| `auth_totp_errors` | > 5/min | TOTP 验证失败多 |
|
||
|
||
### 9.2 日志分析
|
||
|
||
关键日志字段:
|
||
- `user_id`:用户 ID
|
||
- `action`:认证动作
|
||
- `ip`:客户端 IP
|
||
- `user_agent`:客户端标识
|
||
- `success`:是否成功
|
||
|
||
## 10. 总结
|
||
|
||
认证与授权模块设计完善,具有以下特点:
|
||
|
||
- **多层认证**:支持 JWT、API Key、OAuth、TOTP
|
||
- **安全加固**:密码 bcrypt、密钥加密、速率限制
|
||
- **高性能缓存**:两级缓存提升验证效率
|
||
- **完整审计**:全面的认证事件日志
|
||
|
||
**潜在问题:**
|
||
1. 激活码和 API Key 验证未包含系统标识,可能被其他部署实例使用
|
||
2. 缺少登录尝试锁定机制(连续失败后临时封禁)
|
||
|
||
**修改建议:**
|
||
- 如需解决跨实例使用问题,需在 Key 中嵌入实例标识
|
||
- 添加登录失败锁定机制增强安全性
|
||
|
||
---
|
||
*文档版本:1.0*
|
||
*最后更新:2025-01*
|
||
*分析基于:Sub2API v0.1.104* |