Files
junhong_cmp_fiber/docs/auth-architecture.md
huang 18f35f3ef4 feat: 完成B端认证系统和商户管理模块测试补全
主要变更:
- 新增B端认证系统(后台+H5):登录、登出、Token刷新、密码修改
- 完善商户管理和商户账号管理功能
- 补全单元测试(ShopService: 72.5%, ShopAccountService: 79.8%)
- 新增集成测试(商户管理+商户账号管理)
- 归档OpenSpec提案(add-shop-account-management, implement-b-end-auth-system)
- 完善文档(使用指南、API文档、认证架构说明)

测试统计:
- 13个测试套件,37个测试用例,100%通过率
- 平均覆盖率76.2%,达标

OpenSpec验证:通过(strict模式)
2026-01-15 18:15:17 +08:00

11 KiB
Raw Blame History

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 保证,可靠性高

架构图

认证流程

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_token24h
    TokenMgr->>Redis: 存储 refresh_token7天
    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. TokenManagerToken 管理器)

职责

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

数据结构

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 撤销)

依赖注入

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

配置示例

// 后台认证中间件
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防止彩虹表攻击
  • 慢哈希算法,增加暴力破解成本
// 密码哈希(注册时)
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 10)

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

2. Token 安全

不可预测性

  • 使用 UUID v4 生成128 位随机数
  • 碰撞概率极低(约 1/2^122

短生命周期

  • Access Token24 小时自动过期
  • Refresh Token7 天自动过期
  • 修改密码后立即撤销所有旧 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
撤销能力 立即生效 无法撤销
性能 5msRedis 查询) 0ms本地验证
存储负担 ⚠️ Redis 内存 无服务端存储
灵活性 可存储复杂信息 ⚠️ Payload 有大小限制
适用场景 B 端系统(需要撤销) C 端系统(高并发)

决策理由

  • B 端用户数量有限(< 1000Redis 内存负担可接受
  • 修改密码、账号禁用等场景需要立即撤销 Token
  • 需要存储完整的用户上下文信息ShopID、EnterpriseID 等)

为什么使用双令牌机制?

问题:如果只有一个 Token

  • 短生命周期:用户频繁掉线,体验差
  • 长生命周期Token 泄露风险增加

解决方案

  • Access Token24小时用于 API 访问,频繁传输,短生命周期降低泄露风险
  • Refresh Token7天用于刷新 Access Token低频传输长生命周期减少掉线

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

安全原因

  • 假设:用户发现密码泄露,立即修改密码
  • 如果不撤销旧 Token攻击者仍可使用旧 Token 访问

实现

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 验证:< 5msRedis GET 操作)
  • Token 生成:< 10msRedis SET + SADD 操作)
  • Token 撤销:< 5msRedis DEL 操作)

数据库查询优化

登录流程优化

  1. 账号查询:使用 usernamephone 索引(< 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 撤销(单个/批量)
  • 密码修改
  • 账号状态变更

日志格式

{
  "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
}

相关文档


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