Files
backend/.qoder/repowiki/zh/content/认证与授权/JWT认证.md
lan a4b6c5011e
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
chore(git): 更新.gitignore以忽略新的本地文件
2025-11-30 08:33:17 +08:00

10 KiB
Raw Blame History

JWT认证

**本文档引用的文件** - [jwt.go](file://pkg/auth/jwt.go) - [manager.go](file://pkg/auth/manager.go) - [auth.go](file://internal/middleware/auth.go) - [auth_handler.go](file://internal/handler/auth_handler.go) - [user_service.go](file://internal/service/user_service.go) - [config.go](file://pkg/config/config.go)

目录

  1. 简介
  2. JWT服务结构设计
  3. 令牌生成与验证机制
  4. 认证中间件实现
  5. JWT工作流程
  6. 密钥管理与安全策略
  7. 单例模式应用
  8. 最佳实践

简介

CarrotSkin项目采用基于JWTJSON Web Token的用户认证机制为系统提供安全、无状态的用户身份验证。该机制通过在客户端和服务器之间传递加密的令牌来验证用户身份避免了传统会话存储的服务器状态管理开销。本文档详细阐述了JWT服务的实现原理、核心组件设计以及在Gin框架中的集成方式为开发者提供全面的技术参考。

JWT服务结构设计

JWT服务的核心是JWTService结构体,它封装了令牌生成和验证所需的所有配置和方法。该服务通过Claims结构体定义了令牌中包含的用户声明信息。

classDiagram
class JWTService {
-secretKey string
-expireHours int
+GenerateToken(userID int64, username string, role string) (string, error)
+ValidateToken(tokenString string) (*Claims, error)
}
class Claims {
+UserID int64
+Username string
+Role string
+RegisteredClaims jwt.RegisteredClaims
}
JWTService --> Claims : "包含"

Diagram sources

Section sources

令牌生成与验证机制

令牌生成GenerateToken

GenerateToken方法负责创建JWT令牌它接收用户ID、用户名和角色作为参数构建包含这些信息的声明Claims并使用预设的密钥进行签名。

flowchart TD
Start([开始生成令牌]) --> CreateClaims["创建声明对象<br/>包含user_id、username、role"]
CreateClaims --> SetExpiry["设置过期时间<br/>(当前时间 + expireHours)"]
SetExpiry --> SetIssued["设置签发时间<br/>(当前时间)"]
SetIssued --> SetNotBefore["设置生效时间<br/>(当前时间)"]
SetNotBefore --> SetIssuer["设置发行者<br/>(carrotskin)"]
SetIssuer --> CreateToken["创建JWT令牌<br/>使用HS256算法"]
CreateToken --> SignToken["使用secretKey签名"]
SignToken --> ReturnToken["返回签名后的令牌字符串"]
ReturnToken --> End([结束])

Diagram sources

令牌验证ValidateToken

ValidateToken方法负责验证接收到的JWT令牌的有效性包括签名验证、过期检查和声明解析。

flowchart TD
Start([开始验证令牌]) --> ParseToken["解析令牌字符串"]
ParseToken --> CheckSignature["验证签名<br/>使用secretKey"]
CheckSignature --> IsValid{"签名有效?"}
IsValid --> |否| ReturnError["返回错误<br/>无效的token"]
IsValid --> |是| CheckClaims["检查声明对象"]
CheckClaims --> IsClaimsValid{"声明类型正确<br/>且令牌有效?"}
IsClaimsValid --> |否| ReturnError
IsClaimsValid --> |是| ReturnClaims["返回解析出的Claims对象"]
ReturnClaims --> End([结束])
ReturnError --> End

Diagram sources

Section sources

认证中间件实现

CarrotSkin项目提供了两种认证中间件强制认证中间件AuthMiddleware和可选认证中间件OptionalAuthMiddleware它们在Gin框架中拦截HTTP请求并处理Authorization头。

强制认证中间件AuthMiddleware

sequenceDiagram
participant Client as "客户端"
participant Middleware as "AuthMiddleware"
participant JWTService as "JWTService"
Client->>Middleware : 发送请求<br/>Authorization : Bearer token
Middleware->>Middleware : 获取Authorization头
alt 头部为空
Middleware-->>Client : 返回401<br/>缺少Authorization头
return
end
Middleware->>Middleware : 解析Bearer格式
alt 格式无效
Middleware-->>Client : 返回401<br/>无效的Authorization头格式
return
end
Middleware->>JWTService : 调用ValidateToken(token)
alt 验证失败
JWTService-->>Middleware : 返回错误
Middleware-->>Client : 返回401<br/>无效的token
return
end
JWTService-->>Middleware : 返回Claims对象
Middleware->>Middleware : 将用户信息存入上下文<br/>(user_id, username, role)
Middleware->>Client : 继续处理请求

可选认证中间件OptionalAuthMiddleware

sequenceDiagram
participant Client as "客户端"
participant Middleware as "OptionalAuthMiddleware"
participant JWTService as "JWTService"
Client->>Middleware : 发送请求<br/>Authorization : Bearer token
Middleware->>Middleware : 获取Authorization头
alt 头部存在
Middleware->>Middleware : 解析Bearer格式
alt 格式正确
Middleware->>JWTService : 调用ValidateToken(token)
alt 验证成功
JWTService-->>Middleware : 返回Claims对象
Middleware->>Middleware : 将用户信息存入上下文
end
end
end
Middleware->>Client : 继续处理请求<br/>(无论认证成功与否)

Diagram sources

Section sources

JWT工作流程

以下是用户从登录到访问受保护资源的完整JWT工作流程

sequenceDiagram
participant User as "用户"
participant Frontend as "前端"
participant Backend as "后端"
User->>Frontend : 输入用户名/邮箱和密码
Frontend->>Backend : POST /api/v1/auth/login
Backend->>Backend : 调用LoginUser服务
Backend->>Backend : 验证用户凭证
alt 验证成功
Backend->>Backend : 调用JWTService.GenerateToken
Backend->>Backend : 生成JWT令牌
Backend-->>Frontend : 返回200<br/>{token, userInfo}
Frontend->>Frontend : 存储令牌
User->>Frontend : 访问受保护页面
Frontend->>Backend : GET /api/v1/protected<br/>Authorization : Bearer token
Backend->>Backend : AuthMiddleware拦截请求
Backend->>Backend : 解析Authorization头
Backend->>Backend : 调用JWTService.ValidateToken
Backend->>Backend : 验证令牌有效性
Backend->>Backend : 提取用户信息存入上下文
Backend->>Backend : 继续处理业务逻辑
Backend-->>Frontend : 返回请求数据
else 验证失败
Backend-->>Frontend : 返回401<br/>登录失败
end

Diagram sources

Section sources

密钥管理与安全策略

配置管理

JWT服务的配置通过config.go文件中的JWTConfig结构体进行管理,支持环境变量注入,确保密钥的安全性。

classDiagram
class JWTConfig {
+Secret string
+ExpireHours int
}
class Config {
+Server ServerConfig
+Database DatabaseConfig
+Redis RedisConfig
+JWT JWTConfig
+Casbin CasbinConfig
+Log LogConfig
+Upload UploadConfig
+Email EmailConfig
}
Config --> JWTConfig : "包含"

Diagram sources

安全声明

JWT令牌中包含的声明Claims具有重要的安全意义

  • user_id: 用户唯一标识,用于在系统中识别用户身份
  • username: 用户名,用于显示和审计
  • role: 用户角色用于基于角色的访问控制RBAC
  • ExpiresAt: 过期时间,防止令牌长期有效
  • IssuedAt: 签发时间,用于审计和令牌生命周期管理
  • NotBefore: 生效时间,可用于延迟生效的令牌
  • Issuer: 发行者,确保令牌来自可信源

Section sources

单例模式应用

MustGetJWTService函数实现了线程安全的单例模式确保在整个应用程序生命周期中只存在一个JWT服务实例。

sequenceDiagram
participant Init as "初始化"
participant Get as "GetJWTService"
participant MustGet as "MustGetJWTService"
participant Once as "sync.Once"
Init->>Once : 调用Init(cfg)
Once->>Once : 检查是否已执行
alt 首次调用
Once->>Init : 执行初始化
Init->>jwtServiceInstance : 创建JWTService实例
Init-->>Once : 标记已执行
else 非首次调用
Once-->>Init : 跳过执行
end
Get->>Get : 检查jwtServiceInstance
alt 实例存在
Get-->>Get : 返回实例
else 实例不存在
Get-->>Get : 返回错误
end
MustGet->>Get : 调用GetJWTService
alt 获取成功
Get-->>MustGet : 返回实例
MustGet-->>MustGet : 返回实例
else 获取失败
Get-->>MustGet : 返回错误
MustGet->>MustGet : panic(错误)
end

Diagram sources

Section sources

最佳实践

密钥管理

  • JWT密钥secretKey)应通过环境变量配置,避免硬编码在代码中
  • 使用足够长度的随机字符串作为密钥,提高安全性
  • 定期轮换密钥(虽然当前实现不支持,但架构上可以扩展)

过期策略

  • 当前默认过期时间为168小时7天可通过JWT_EXPIRE_HOURS环境变量调整
  • 对于敏感操作,建议使用短期令牌或结合刷新令牌机制
  • 过期时间不宜过长,以降低令牌泄露后的风险

防止重放攻击

  • 虽然JWT本身是无状态的但可以通过以下方式增强安全性
    • 使用短期令牌
    • 在Redis中维护已注销令牌的黑名单
    • 结合客户端指纹如IP地址、User-Agent进行额外验证
  • 当前实现中,OptionalAuthMiddleware提供了基础的灵活性,允许部分接口在认证失败时仍能访问

性能考虑

  • JWT验证是计算密集型操作但在现代硬件上性能良好
  • 使用HS256算法在安全性和性能之间取得了良好平衡
  • 避免在令牌中存储过多信息,以减小令牌大小和传输开销

Section sources