406 lines
9.4 KiB
Markdown
406 lines
9.4 KiB
Markdown
# JWT 认证系统文档
|
||
|
||
## 🔐 认证架构
|
||
|
||
Nebula 项目使用 **JWT (JSON Web Token)** 认证系统,提供完整的用户认证和授权功能。
|
||
|
||
### 核心特性
|
||
|
||
- ✅ **JWT 双 Token 机制**(Access Token + Refresh Token)
|
||
- ✅ **密码加密存储**(bcrypt)
|
||
- ✅ **灵活的 Token 过期时间配置**
|
||
- ✅ **中间件保护路由**
|
||
- ✅ **角色权限支持**(user/admin)
|
||
- ✅ **用户注册和登录**
|
||
- ✅ **修改密码**
|
||
- ✅ **Token 刷新**
|
||
|
||
## 📝 API 端点
|
||
|
||
### 1. 用户注册
|
||
|
||
```http
|
||
POST /api/auth/register
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"username": "testuser",
|
||
"email": "test@example.com",
|
||
"password": "password123"
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": {
|
||
"user": {
|
||
"id": "uuid",
|
||
"username": "testuser",
|
||
"email": "test@example.com",
|
||
"role": "user",
|
||
"createdAt": "2026-03-10T12:00:00Z",
|
||
"updatedAt": "2026-03-10T12:00:00Z"
|
||
},
|
||
"tokens": {
|
||
"access_token": "eyJhbGciOiJIUzI1NiIs...",
|
||
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
|
||
"expires_in": 7200
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 用户登录
|
||
|
||
```http
|
||
POST /api/auth/login
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"username": "testuser",
|
||
"password": "password123"
|
||
}
|
||
```
|
||
|
||
**响应:** 同注册响应
|
||
|
||
### 3. 刷新 Token
|
||
|
||
```http
|
||
POST /api/auth/refresh
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": {
|
||
"access_token": "new_access_token",
|
||
"refresh_token": "new_refresh_token",
|
||
"expires_in": 7200
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 获取当前用户信息
|
||
|
||
```http
|
||
GET /api/auth/profile
|
||
Authorization: Bearer <access_token>
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": {
|
||
"id": "uuid",
|
||
"username": "testuser",
|
||
"email": "test@example.com",
|
||
"role": "user",
|
||
"createdAt": "2026-03-10T12:00:00Z",
|
||
"updatedAt": "2026-03-10T12:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5. 修改密码
|
||
|
||
```http
|
||
POST /api/auth/change-password
|
||
Authorization: Bearer <access_token>
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"old_password": "password123",
|
||
"new_password": "newpassword456"
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "password changed successfully"
|
||
}
|
||
```
|
||
|
||
## 🧪 PowerShell 测试示例
|
||
|
||
### 完整测试流程
|
||
|
||
```powershell
|
||
# 1. 注册用户
|
||
$registerBody = @{
|
||
username = "testuser"
|
||
email = "test@example.com"
|
||
password = "password123"
|
||
} | ConvertTo-Json
|
||
|
||
$registerResponse = Invoke-RestMethod -Uri "http://localhost:9050/api/auth/register" `
|
||
-Method Post -Body $registerBody -ContentType "application/json"
|
||
|
||
Write-Host "✅ 注册成功!"
|
||
Write-Host "用户 ID: $($registerResponse.data.user.id)"
|
||
Write-Host "Access Token: $($registerResponse.data.tokens.access_token.Substring(0, 20))..."
|
||
|
||
# 保存 token
|
||
$accessToken = $registerResponse.data.tokens.access_token
|
||
$refreshToken = $registerResponse.data.tokens.refresh_token
|
||
|
||
# 2. 登录(测试)
|
||
$loginBody = @{
|
||
username = "testuser"
|
||
password = "password123"
|
||
} | ConvertTo-Json
|
||
|
||
$loginResponse = Invoke-RestMethod -Uri "http://localhost:9050/api/auth/login" `
|
||
-Method Post -Body $loginBody -ContentType "application/json"
|
||
|
||
Write-Host "✅ 登录成功!"
|
||
|
||
# 3. 获取用户信息
|
||
$headers = @{
|
||
Authorization = "Bearer $accessToken"
|
||
}
|
||
|
||
$profile = Invoke-RestMethod -Uri "http://localhost:9050/api/auth/profile" `
|
||
-Method Get -Headers $headers
|
||
|
||
Write-Host "✅ 获取用户信息成功!"
|
||
Write-Host "用户名: $($profile.data.username)"
|
||
Write-Host "邮箱: $($profile.data.email)"
|
||
|
||
# 4. 创建应用(测试认证保护)
|
||
$appBody = @{
|
||
id = "test-app"
|
||
name = "测试应用"
|
||
description = "需要认证才能创建"
|
||
} | ConvertTo-Json
|
||
|
||
$app = Invoke-RestMethod -Uri "http://localhost:9050/api/apps" `
|
||
-Method Post -Body $appBody -ContentType "application/json" -Headers $headers
|
||
|
||
Write-Host "✅ 创建应用成功(认证有效)!"
|
||
|
||
# 5. 刷新 Token
|
||
Start-Sleep -Seconds 2
|
||
$refreshBody = @{
|
||
refresh_token = $refreshToken
|
||
} | ConvertTo-Json
|
||
|
||
$newTokens = Invoke-RestMethod -Uri "http://localhost:9050/api/auth/refresh" `
|
||
-Method Post -Body $refreshBody -ContentType "application/json"
|
||
|
||
Write-Host "✅ Token 刷新成功!"
|
||
|
||
# 6. 修改密码
|
||
$changePasswordBody = @{
|
||
old_password = "password123"
|
||
new_password = "newpassword456"
|
||
} | ConvertTo-Json
|
||
|
||
$changeResult = Invoke-RestMethod -Uri "http://localhost:9050/api/auth/change-password" `
|
||
-Method Post -Body $changePasswordBody -ContentType "application/json" -Headers $headers
|
||
|
||
Write-Host "✅ 密码修改成功!"
|
||
|
||
# 7. 测试未认证访问(应该失败)
|
||
try {
|
||
Invoke-RestMethod -Uri "http://localhost:9050/api/apps" -Method Get
|
||
} catch {
|
||
Write-Host "❌ 未认证访问被拒绝(正确)"
|
||
}
|
||
```
|
||
|
||
## 🔧 配置
|
||
|
||
### 环境变量
|
||
|
||
```bash
|
||
# JWT 密钥(生产环境必须修改!)
|
||
JWT_SECRET=your-secret-key-change-in-production
|
||
|
||
# Access Token 过期时间(默认 2 小时)
|
||
JWT_ACCESS_TOKEN_DURATION=7200 # 秒
|
||
# 或者
|
||
JWT_ACCESS_TOKEN_DURATION=2h
|
||
|
||
# Refresh Token 过期时间(默认 7 天)
|
||
JWT_REFRESH_TOKEN_DURATION=604800 # 秒
|
||
# 或者
|
||
JWT_REFRESH_TOKEN_DURATION=168h
|
||
```
|
||
|
||
### 推荐配置
|
||
|
||
**开发环境:**
|
||
- Access Token: 2 小时
|
||
- Refresh Token: 7 天
|
||
|
||
**生产环境:**
|
||
- Access Token: 15 分钟 - 1 小时
|
||
- Refresh Token: 30 天
|
||
- 强密钥(256位以上)
|
||
|
||
## 🛡️ 安全特性
|
||
|
||
### 1. 密码加密
|
||
|
||
```go
|
||
// 使用 bcrypt 加密
|
||
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||
|
||
// 验证密码
|
||
bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||
```
|
||
|
||
### 2. Token 验证
|
||
|
||
- ✅ 签名验证
|
||
- ✅ 过期时间检查
|
||
- ✅ 算法验证(HMAC-SHA256)
|
||
|
||
### 3. 中间件保护
|
||
|
||
```go
|
||
// 需要认证
|
||
authRequired.Use(auth.JWTMiddleware(jwtService))
|
||
|
||
// 需要管理员权限
|
||
adminRequired.Use(auth.JWTMiddleware(jwtService), auth.AdminMiddleware())
|
||
```
|
||
|
||
## 📊 路由保护状态
|
||
|
||
### 公开路由(无需认证)
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | /api/auth/register | 用户注册 |
|
||
| POST | /api/auth/login | 用户登录 |
|
||
| POST | /api/auth/refresh | 刷新 Token |
|
||
| GET | /api/check-update | 检查更新 |
|
||
|
||
### 需要认证的路由
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | /api/auth/profile | 获取用户信息 |
|
||
| POST | /api/auth/change-password | 修改密码 |
|
||
| GET/POST/PUT/DELETE | /api/apps/* | 应用管理 |
|
||
| GET/POST/PUT/DELETE | /api/releases/* | 版本管理 |
|
||
| GET/POST/PUT/DELETE | /api/assets/* | 资源管理 |
|
||
|
||
## 🔄 Token 刷新流程
|
||
|
||
```
|
||
Client Server
|
||
| |
|
||
|-------- Request API --------->| (Access Token)
|
||
|<----- 401 Token Expired ------| ❌
|
||
| |
|
||
|--- Refresh Token Request ---->| (Refresh Token)
|
||
|<--- New Access Token ---------| ✅
|
||
| |
|
||
|-------- Request API --------->| (New Access Token)
|
||
|<-------- Success -------------| ✅
|
||
```
|
||
|
||
## 💡 客户端集成示例
|
||
|
||
### JavaScript/TypeScript
|
||
|
||
```typescript
|
||
class AuthService {
|
||
private accessToken: string | null = null;
|
||
private refreshToken: string | null = null;
|
||
|
||
async login(username: string, password: string) {
|
||
const response = await fetch('http://localhost:9050/api/auth/login', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username, password })
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.code === 0) {
|
||
this.accessToken = data.data.tokens.access_token;
|
||
this.refreshToken = data.data.tokens.refresh_token;
|
||
|
||
// 保存到 localStorage
|
||
localStorage.setItem('access_token', this.accessToken);
|
||
localStorage.setItem('refresh_token', this.refreshToken);
|
||
}
|
||
return data;
|
||
}
|
||
|
||
async request(url: string, options: RequestInit = {}) {
|
||
options.headers = {
|
||
...options.headers,
|
||
'Authorization': `Bearer ${this.accessToken}`
|
||
};
|
||
|
||
let response = await fetch(url, options);
|
||
|
||
// Token 过期,尝试刷新
|
||
if (response.status === 401) {
|
||
await this.refresh();
|
||
options.headers['Authorization'] = `Bearer ${this.accessToken}`;
|
||
response = await fetch(url, options);
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
|
||
async refresh() {
|
||
const response = await fetch('http://localhost:9050/api/auth/refresh', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ refresh_token: this.refreshToken })
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.code === 0) {
|
||
this.accessToken = data.data.access_token;
|
||
this.refreshToken = data.data.refresh_token;
|
||
localStorage.setItem('access_token', this.accessToken);
|
||
localStorage.setItem('refresh_token', this.refreshToken);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🚀 扩展功能
|
||
|
||
### 未来可添加
|
||
|
||
1. **Token 黑名单**:退出登录时将 token 加入黑名单
|
||
2. **双因素认证(2FA)**:增强安全性
|
||
3. **OAuth2 第三方登录**:支持 GitHub、Google 等
|
||
4. **权限细粒度控制**:基于资源的权限管理
|
||
5. **登录日志**:记录登录历史和异常
|
||
6. **IP 白名单**:限制访问来源
|
||
7. **账号锁定**:多次登录失败后锁定
|
||
|
||
## ✅ 测试验证
|
||
|
||
1. ✅ 注册新用户
|
||
2. ✅ 登录获取 token
|
||
3. ✅ 使用 token 访问受保护的 API
|
||
4. ✅ 刷新 token
|
||
5. ✅ 修改密码
|
||
6. ✅ 未认证访问被拒绝
|
||
|
||
完整的 JWT 认证系统已经实现并运行正常!🎉
|