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

310 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# JWT认证
<cite>
**本文档引用的文件**
- [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)
</cite>
## 目录
1. [简介](#简介)
2. [JWT服务结构设计](#jwt服务结构设计)
3. [令牌生成与验证机制](#令牌生成与验证机制)
4. [认证中间件实现](#认证中间件实现)
5. [JWT工作流程](#jwt工作流程)
6. [密钥管理与安全策略](#密钥管理与安全策略)
7. [单例模式应用](#单例模式应用)
8. [最佳实践](#最佳实践)
## 简介
CarrotSkin项目采用基于JWTJSON Web Token的用户认证机制为系统提供安全、无状态的用户身份验证。该机制通过在客户端和服务器之间传递加密的令牌来验证用户身份避免了传统会话存储的服务器状态管理开销。本文档详细阐述了JWT服务的实现原理、核心组件设计以及在Gin框架中的集成方式为开发者提供全面的技术参考。
## JWT服务结构设计
JWT服务的核心是`JWTService`结构体,它封装了令牌生成和验证所需的所有配置和方法。该服务通过`Claims`结构体定义了令牌中包含的用户声明信息。
```mermaid
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**
- [jwt.go](file://pkg/auth/jwt.go#L11-L14)
- [jwt.go](file://pkg/auth/jwt.go#L25-L30)
**Section sources**
- [jwt.go](file://pkg/auth/jwt.go#L11-L71)
## 令牌生成与验证机制
### 令牌生成GenerateToken
`GenerateToken`方法负责创建JWT令牌它接收用户ID、用户名和角色作为参数构建包含这些信息的声明Claims并使用预设的密钥进行签名。
```mermaid
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**
- [jwt.go](file://pkg/auth/jwt.go#L33-L53)
### 令牌验证ValidateToken
`ValidateToken`方法负责验证接收到的JWT令牌的有效性包括签名验证、过期检查和声明解析。
```mermaid
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**
- [jwt.go](file://pkg/auth/jwt.go#L56-L70)
**Section sources**
- [jwt.go](file://pkg/auth/jwt.go#L32-L70)
## 认证中间件实现
CarrotSkin项目提供了两种认证中间件强制认证中间件AuthMiddleware和可选认证中间件OptionalAuthMiddleware它们在Gin框架中拦截HTTP请求并处理Authorization头。
### 强制认证中间件AuthMiddleware
```mermaid
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
```mermaid
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**
- [auth.go](file://internal/middleware/auth.go#L13-L55)
- [auth.go](file://internal/middleware/auth.go#L58-L78)
**Section sources**
- [auth.go](file://internal/middleware/auth.go#L1-L79)
## JWT工作流程
以下是用户从登录到访问受保护资源的完整JWT工作流程
```mermaid
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**
- [auth_handler.go](file://internal/handler/auth_handler.go#L97-L147)
- [user_service.go](file://internal/service/user_service.go#L71-L122)
- [auth.go](file://internal/middleware/auth.go#L13-L55)
**Section sources**
- [auth_handler.go](file://internal/handler/auth_handler.go#L97-L147)
- [user_service.go](file://internal/service/user_service.go#L71-L122)
## 密钥管理与安全策略
### 配置管理
JWT服务的配置通过`config.go`文件中的`JWTConfig`结构体进行管理,支持环境变量注入,确保密钥的安全性。
```mermaid
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**
- [config.go](file://pkg/config/config.go#L68-L71)
- [config.go](file://pkg/config/config.go#L14-L24)
### 安全声明
JWT令牌中包含的声明Claims具有重要的安全意义
- **user_id**: 用户唯一标识,用于在系统中识别用户身份
- **username**: 用户名,用于显示和审计
- **role**: 用户角色用于基于角色的访问控制RBAC
- **ExpiresAt**: 过期时间,防止令牌长期有效
- **IssuedAt**: 签发时间,用于审计和令牌生命周期管理
- **NotBefore**: 生效时间,可用于延迟生效的令牌
- **Issuer**: 发行者,确保令牌来自可信源
**Section sources**
- [jwt.go](file://pkg/auth/jwt.go#L25-L30)
- [config.go](file://pkg/config/config.go#L68-L71)
## 单例模式应用
`MustGetJWTService`函数实现了线程安全的单例模式确保在整个应用程序生命周期中只存在一个JWT服务实例。
```mermaid
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**
- [manager.go](file://pkg/auth/manager.go#L9-L45)
**Section sources**
- [manager.go](file://pkg/auth/manager.go#L9-L45)
## 最佳实践
### 密钥管理
- JWT密钥`secretKey`)应通过环境变量配置,避免硬编码在代码中
- 使用足够长度的随机字符串作为密钥,提高安全性
- 定期轮换密钥(虽然当前实现不支持,但架构上可以扩展)
### 过期策略
- 当前默认过期时间为168小时7天可通过`JWT_EXPIRE_HOURS`环境变量调整
- 对于敏感操作,建议使用短期令牌或结合刷新令牌机制
- 过期时间不宜过长,以降低令牌泄露后的风险
### 防止重放攻击
- 虽然JWT本身是无状态的但可以通过以下方式增强安全性
- 使用短期令牌
- 在Redis中维护已注销令牌的黑名单
- 结合客户端指纹如IP地址、User-Agent进行额外验证
- 当前实现中,`OptionalAuthMiddleware`提供了基础的灵活性,允许部分接口在认证失败时仍能访问
### 性能考虑
- JWT验证是计算密集型操作但在现代硬件上性能良好
- 使用HS256算法在安全性和性能之间取得了良好平衡
- 避免在令牌中存储过多信息,以减小令牌大小和传输开销
**Section sources**
- [config.go](file://pkg/config/config.go#L164)
- [manager.go](file://pkg/auth/manager.go#L18-L23)
- [jwt.go](file://pkg/auth/jwt.go#L39-L43)