主要变更: - 新增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模式)
11 KiB
11 KiB
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_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
数据结构:
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 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 访问
实现:
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 操作)
数据库查询优化
登录流程优化:
- 账号查询:使用
username或phone索引(< 10ms) - 权限查询:使用
account_id索引(< 20ms) - 总耗时:< 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
维护者: 君鸿卡管系统开发团队