# B 端认证系统架构说明

本文档描述君鸿卡管系统 B 端认证的架构设计、技术决策和安全机制。

---

## 系统概述

### 核心特性

- **双令牌机制**：Access Token（短期）+ Refresh Token（长期）
- **Redis 存储**：Token 存储在 Redis，支持快速撤销
- **多平台支持**：后台管理（Admin）和 H5 移动端
- **用户类型隔离**：不同平台限制不同的用户类型访问
- **无状态验证**：Token 验证无需查询数据库

### 技术栈

| 组件 | 技术选型 | 理由 |
|------|----------|------|
| Token 生成 | UUID v4 | 高度随机，不可预测 |
| Token 存储 | Redis | 快速查询，支持 TTL 自动过期 |
| 密码哈希 | bcrypt | 慢哈希算法，抗暴力破解 |
| HTTP 框架 | Fiber v2 | 高性能，类 Express API |
| 数据库 | PostgreSQL | ACID 保证，可靠性高 |

---

## 架构图

### 认证流程

```mermaid
sequenceDiagram
    participant Client as 客户端
    participant Handler as AuthHandler
    participant Service as AuthService
    participant TokenMgr as TokenManager
    participant Redis as Redis
    participant DB as PostgreSQL

    Note over Client,DB: 1. 登录流程
    Client->>Handler: POST /api/admin/login
    Handler->>Service: Login(username, password)
    Service->>DB: 查询账号信息
    DB-->>Service: 返回账号（含密码哈希）
    Service->>Service: bcrypt 验证密码
    Service->>DB: 查询用户权限
    DB-->>Service: 返回权限列表
    Service->>TokenMgr: GenerateTokenPair(userInfo)
    TokenMgr->>Redis: 存储 access_token（24h）
    TokenMgr->>Redis: 存储 refresh_token（7天）
    TokenMgr-->>Service: 返回 token 对
    Service-->>Handler: 返回 token + 用户信息
    Handler-->>Client: 200 OK + JSON响应

    Note over Client,DB: 2. 访问受保护接口
    Client->>Handler: GET /api/admin/me + Bearer Token
    Handler->>TokenMgr: ValidateAccessToken(token)
    TokenMgr->>Redis: GET auth:token:{token}
    Redis-->>TokenMgr: 返回 TokenInfo
    TokenMgr-->>Handler: 返回用户上下文
    Handler->>Service: GetCurrentUser(userID)
    Service->>DB: 查询用户信息
    DB-->>Service: 返回用户数据
    Service-->>Handler: 返回用户+权限
    Handler-->>Client: 200 OK + JSON响应

    Note over Client,DB: 3. Token 刷新
    Client->>Handler: POST /api/admin/refresh-token
    Handler->>Service: RefreshToken(refresh_token)
    Service->>TokenMgr: ValidateRefreshToken(token)
    TokenMgr->>Redis: GET auth:refresh:{token}
    Redis-->>TokenMgr: 返回 TokenInfo
    TokenMgr->>TokenMgr: GenerateNewAccessToken
    TokenMgr->>Redis: 存储新 access_token
    TokenMgr-->>Service: 返回新 access_token
    Service-->>Handler: 返回新 token
    Handler-->>Client: 200 OK + new token
```

### 中间件执行顺序

```
HTTP 请求
    ↓
[Recover 中间件]
    ↓
[RequestID 中间件]
    ↓
[Logger 中间件]
    ↓
[Auth 中间件]  ← 本系统
    ├─ 提取 Token
    ├─ 验证 Token（调用 TokenManager）
    ├─ 检查用户类型
    └─ 设置用户上下文
    ↓
[路由处理器]
    ├─ 从 context 获取用户信息
    └─ 执行业务逻辑
    ↓
HTTP 响应
```

---

## 核心组件设计

### 1. TokenManager（Token 管理器）

**职责**：
- Token 生成：使用 UUID v4 生成不可预测的 Token
- Token 验证：从 Redis 查询并解析 TokenInfo
- Token 撤销：单个撤销或批量撤销用户所有 Token
- Token 刷新：验证 Refresh Token 并生成新的 Access Token

**数据结构**：

```go
type TokenInfo struct {
    UserID       uint      // 用户 ID
    UserType     int       // 用户类型（1-4）
    ShopID       uint      // 店铺 ID（代理商）
    EnterpriseID uint      // 企业 ID（企业客户）
    Username     string    // 用户名
    LoginTime    time.Time // 登录时间
    Device       string    // 设备类型
    IP           string    // 登录 IP
}
```

**Redis 存储结构**：

```
# Access Token
Key:   auth:token:{token_uuid}
Value: JSON(TokenInfo)
TTL:   24 小时

# Refresh Token
Key:   auth:refresh:{token_uuid}
Value: JSON(TokenInfo)
TTL:   7 天

# 用户 Token 列表（用于批量撤销）
Key:   auth:user:{user_id}:tokens
Value: SET[token1, token2, ...]
TTL:   7 天
```

### 2. AuthService（认证服务）

**职责**：
- 登录验证：查询账号、验证密码、生成 Token
- 权限查询：查询用户的角色和权限列表
- Token 管理：登出、刷新、批量撤销
- 密码管理：修改密码（含旧 Token 撤销）

**依赖注入**：

```go
type Service struct {
    accountStore     *postgres.AccountStore     // 账号查询
    accountRoleStore *postgres.AccountRoleStore // 账号-角色关联
    rolePermStore    *postgres.RolePermissionStore // 角色-权限关联
    permissionStore  *postgres.PermissionStore  // 权限查询
    tokenManager     *auth.TokenManager         // Token 管理
    logger           *zap.Logger                // 日志记录
}
```

### 3. Auth Middleware（认证中间件）

**职责**：
- Token 提取：从 `Authorization: Bearer {token}` 提取 Token
- Token 验证：调用 TokenManager 验证合法性
- 用户类型检查：根据平台限制用户类型
- 上下文设置：将用户信息设置到 Fiber 和 Go Context

**配置示例**：

```go
// 后台认证中间件
AdminAuth := middleware.Auth(middleware.AuthConfig{
    TokenValidator: func(token string) (*middleware.UserContextInfo, error) {
        // 验证 token
        tokenInfo, err := tokenManager.ValidateAccessToken(ctx, token)
        if err != nil {
            return nil, errors.New(errors.CodeInvalidToken, "令牌无效")
        }

        // 检查用户类型：后台只允许 SuperAdmin、Platform、Agent
        if tokenInfo.UserType != constants.UserTypeSuperAdmin &&
           tokenInfo.UserType != constants.UserTypePlatform &&
           tokenInfo.UserType != constants.UserTypeAgent {
            return nil, errors.New(errors.CodeForbidden, "权限不足")
        }

        return &middleware.UserContextInfo{...}, nil
    },
    SkipPaths: []string{"/api/admin/login", "/api/admin/refresh-token"},
})
```

---

## 安全机制

### 1. 密码安全

**Bcrypt 哈希**：
- 使用 bcrypt 算法（cost=10）存储密码
- 每个密码有唯一的 salt，防止彩虹表攻击
- 慢哈希算法，增加暴力破解成本

```go
// 密码哈希（注册时）
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 10)

// 密码验证（登录时）
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
```

### 2. Token 安全

**不可预测性**：
- 使用 UUID v4 生成，128 位随机数
- 碰撞概率极低（约 1/2^122）

**短生命周期**：
- Access Token：24 小时自动过期
- Refresh Token：7 天自动过期
- 修改密码后立即撤销所有旧 Token

**传输安全**：
- 仅通过 Authorization 请求头传递（不在 URL 中）
- 生产环境强制 HTTPS

### 3. 用户类型隔离

| 平台 | 允许访问 | 拒绝访问 |
|------|----------|----------|
| 后台 | SuperAdmin(1), Platform(2), Agent(3) | Enterprise(4), PersonalCustomer |
| H5   | Agent(3), Enterprise(4) | SuperAdmin(1), Platform(2), PersonalCustomer |

### 4. 防御措施

**防止暴力破解**：
- 计划引入登录失败次数限制（待实现）
- 使用慢哈希算法（bcrypt）增加单次尝试成本

**防止 Token 泄露**：
- Token 不出现在日志中（敏感信息脱敏）
- Token 不出现在 URL 中
- Redis 连接使用密码保护

**防止会话劫持**：
- Token 绑定设备和 IP（存储在 TokenInfo 中，可用于审计）
- 可选：实现设备指纹验证（待实现）

---

## 设计决策

### 为什么选择 Redis 而非 JWT？

| 对比项 | Redis Token | JWT |
|--------|-------------|-----|
| 撤销能力 | ✅ 立即生效 | ❌ 无法撤销 |
| 性能 | ✅ 5ms（Redis 查询） | ✅ 0ms（本地验证） |
| 存储负担 | ⚠️ Redis 内存 | ✅ 无服务端存储 |
| 灵活性 | ✅ 可存储复杂信息 | ⚠️ Payload 有大小限制 |
| 适用场景 | B 端系统（需要撤销） | C 端系统（高并发） |

**决策理由**：
- B 端用户数量有限（< 1000），Redis 内存负担可接受
- 修改密码、账号禁用等场景需要立即撤销 Token
- 需要存储完整的用户上下文信息（ShopID、EnterpriseID 等）

### 为什么使用双令牌机制？

**问题**：如果只有一个 Token：
- 短生命周期：用户频繁掉线，体验差
- 长生命周期：Token 泄露风险增加

**解决方案**：
- Access Token（24小时）：用于 API 访问，频繁传输，短生命周期降低泄露风险
- Refresh Token（7天）：用于刷新 Access Token，低频传输，长生命周期减少掉线

### 为什么密码修改要撤销所有 Token？

**安全原因**：
- 假设：用户发现密码泄露，立即修改密码
- 如果不撤销旧 Token，攻击者仍可使用旧 Token 访问

**实现**：
```go
func (s *Service) ChangePassword(ctx context.Context, userID uint, oldPassword, newPassword string) error {
    // 1. 验证旧密码
    // 2. 哈希新密码
    // 3. 更新数据库
    // 4. 撤销所有旧 Token
    return s.tokenManager.RevokeAllUserTokens(ctx, userID)
}
```

---

## 性能考量

### Redis 性能

**预期负载**：
- 用户数：< 1000
- 每用户平均 Token 数：2-3 个
- 总 Token 数：< 3000
- Redis 内存占用：< 3MB（每个 TokenInfo 约 1KB）

**性能指标**：
- Token 验证：< 5ms（Redis GET 操作）
- Token 生成：< 10ms（Redis SET + SADD 操作）
- Token 撤销：< 5ms（Redis DEL 操作）

### 数据库查询优化

**登录流程优化**：
1. 账号查询：使用 `username` 或 `phone` 索引（< 10ms）
2. 权限查询：使用 `account_id` 索引（< 20ms）
3. 总耗时：< 50ms

**缓存策略**（待实现）：
- 用户权限列表可缓存 30 分钟
- 减少数据库查询压力

---

## 扩展性

### 水平扩展

**无状态设计**：
- 认证服务无状态，可水平扩展
- Token 存储在 Redis，所有实例共享

**Redis 集群**：
- 当前使用单机 Redis
- 需要时可升级为 Redis Cluster 或 Sentinel

### 功能扩展

**可选功能**：
- [ ] 设备指纹验证
- [ ] 登录失败次数限制
- [ ] 异地登录提醒
- [ ] 在线设备管理
- [ ] Token 黑名单

---

## 监控和审计

### 关键指标

| 指标 | 说明 | 告警阈值 |
|------|------|----------|
| 登录成功率 | 成功次数 / 总次数 | < 95% |
| Token 验证失败率 | 失败次数 / 总次数 | > 5% |
| Redis 可用性 | Ping 响应时间 | > 10ms |
| Token 平均验证时间 | P95 响应时间 | > 20ms |

### 审计日志

**记录事件**：
- 用户登录（成功/失败）
- Token 撤销（单个/批量）
- 密码修改
- 账号状态变更

**日志格式**：

```json
{
  "level": "info",
  "timestamp": "2026-01-15T16:15:00+08:00",
  "event": "user_login",
  "user_id": 1,
  "username": "admin",
  "ip": "127.0.0.1",
  "device": "web",
  "success": true
}
```

---

## 相关文档

- [API 文档](api/auth.md) - 完整的 API 接口说明
- [使用指南](auth-usage-guide.md) - 如何在代码中集成认证
- [错误处理指南](003-error-handling/使用指南.md) - 统一错误处理

---

**文档版本**: v1.0  
**最后更新**: 2026-01-15  
**维护者**: 君鸿卡管系统开发团队
