Files
nebula/docs/AUTH_JWT.md
2026-03-10 16:26:48 +08:00

406 lines
9.4 KiB
Markdown
Raw Permalink 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 认证系统文档
## 🔐 认证架构
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 认证系统已经实现并运行正常!🎉