Files
data-sync-service/TOKEN_AUTH_REQUIREMENT.md
K-hermes 2cf807b59c feat: 实现 Token 认证系统
- 后端:Lua 实现 Token 验证、权限控制、限流、审计日志
- API:创建/列出/查看/更新/删除 Token
- Web UI:Token 管理界面(列表、创建、详情、编辑、删除弹框)
- 向后兼容:保留 X-Password 超级管理员认证
- 安全增强:失败次数限制、过期检测、双因素验证(敏感操作)

需求文档:TOKEN_AUTH_REQUIREMENT.md
2026-09-13 23:40:42 +08:00

701 lines
19 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.
# Token 认证系统需求文档
## 1. 项目背景
当前数据同步服务使用单一密码 `admin123123123` 进行认证,存在以下问题:
- 无法区分不同客户端的访问
- 无法追踪访问来源和使用情况
- 泄露后需要全局修改所有客户端配置
- 无法对不同客户端进行细粒度的权限控制
## 2. 核心目标
在保持向后兼容的前提下,增加 Token 认证机制,实现:
- 多 Token 管理(不同客户端使用不同 Token)
- 访问审计(记录每个 Token 的使用情况)
- 权限控制(只读/读写)
- 安全增强(失败次数限制、限流、双因素验证)
## 3. 功能需求
### 3.1 Token 设计
#### Token 生成规则
- **格式**:`dss_` + 32位随机字符串(十六进制)
- **示例**:`dss_a1b2c3d4e5f6789012345678901234ab`
- **唯一性**:每个 Token 全局唯一
#### Token 属性
```json
{
"token": "dss_a1b2c3d4e5f6789012345678901234ab",
"name": "生产服务器A",
"permission": "readonly",
"status": "active",
"created_at": "2026-09-13 10:30:00",
"expires_at": null,
"stats": {
"total_requests": 1523,
"last_used_at": "2026-09-13 15:20:10",
"last_ip": "192.168.1.100",
"failed_attempts": 0
},
"access_log": []
}
```
**字段说明**:
- `token`: Token 字符串
- `name`: 用户自定义名称(必填,用于识别客户端)
- `permission`: 权限级别
- `readonly`: 只读(只能 GET)
- `readwrite`: 读写(可 GET/POST/DELETE)
- `status`: Token 状态
- `active`: 启用
- `disabled`: 禁用
- `expired`: 已过期
- `created_at`: 创建时间
- `expires_at`: 过期时间(null 表示永久有效)
- `stats.total_requests`: 累计请求次数
- `stats.last_used_at`: 最后使用时间
- `stats.last_ip`: 最后使用的 IP 地址
- `stats.failed_attempts`: 连续失败次数(达到 5 次自动禁用)
- `access_log`: 访问日志数组(保留最近 20 条)
#### 访问日志格式
```json
{
"timestamp": "2026-09-13 15:20:10",
"ip": "192.168.1.100",
"method": "GET",
"path": "/api/data/weibo:ykaayk",
"status": 200,
"error": null
}
```
### 3.2 API 设计
#### 认证方式
**新方式(推荐)**:
```http
GET /api/data/weibo:ykaayk
X-Token: dss_a1b2c3d4e5f6789012345678901234ab
```
**旧方式(兼容保留)**:
```http
GET /api/data/weibo:ykaayk
X-Password: admin123123123
```
**说明**:
- `X-Password` 作为超级管理员密码,拥有所有权限,不计入审计日志
- `X-Token` 为普通 Token,受权限和限流控制,记录审计日志
- 两种方式可以并存,优先检查 `X-Token`
#### Token 管理 API
##### 1. 创建 Token
```http
POST /api/tokens
X-Password: admin123123123
Content-Type: application/json
{
"name": "生产服务器A",
"permission": "readonly",
"expires_in": null
}
```
**参数说明**:
- `name`: Token 名称(必填)
- `permission`: 权限(必填,`readonly` 或 `readwrite`)
- `expires_in`: 过期时间(秒,可选,null 表示永久)
- 2592000 = 30天
- 7776000 = 90天
**响应**:
```json
{
"success": true,
"token": "dss_a1b2c3d4e5f6789012345678901234ab",
"name": "生产服务器A",
"permission": "readonly",
"created_at": "2026-09-13 10:30:00",
"expires_at": null
}
```
##### 2. 列出所有 Token
```http
GET /api/tokens
X-Password: admin123123123
```
**响应**:
```json
{
"tokens": [
{
"token": "dss_a1b2c3...(前12位)",
"token_full": "dss_a1b2c3d4e5f6789012345678901234ab",
"name": "生产服务器A",
"permission": "readonly",
"status": "active",
"created_at": "2026-09-13 10:30:00",
"expires_at": null,
"stats": {
"total_requests": 1523,
"last_used_at": "2026-09-13 15:20:10",
"last_ip": "192.168.1.100",
"failed_attempts": 0
}
}
]
}
```
##### 3. 获取 Token 详情
```http
GET /api/tokens/:token
X-Password: admin123123123
```
**响应**:
```json
{
"token": "dss_a1b2c3d4e5f6789012345678901234ab",
"name": "生产服务器A",
"permission": "readonly",
"status": "active",
"created_at": "2026-09-13 10:30:00",
"expires_at": null,
"stats": {
"total_requests": 1523,
"last_used_at": "2026-09-13 15:20:10",
"last_ip": "192.168.1.100",
"failed_attempts": 0
},
"access_log": [
{
"timestamp": "2026-09-13 15:20:10",
"ip": "192.168.1.100",
"method": "GET",
"path": "/api/data/weibo:ykaayk",
"status": 200
}
]
}
```
##### 4. 更新 Token 状态
```http
PATCH /api/tokens/:token
X-Password: admin123123123
Content-Type: application/json
{
"status": "disabled"
}
```
**可更新字段**:
- `status`: `active` 或 `disabled`
- `name`: 重命名
**响应**:
```json
{
"success": true,
"token": "dss_a1b2c3d4e5f6789012345678901234ab",
"status": "disabled"
}
```
##### 5. 删除 Token(需要二次确认)
```http
DELETE /api/tokens/:token
X-Password: admin123123123
Content-Type: application/json
{
"confirm_password": "admin123123123"
}
```
**响应**:
```json
{
"success": true,
"message": "Token deleted",
"token": "dss_a1b2c3d4e5f6789012345678901234ab"
}
```
### 3.3 权限控制
#### 只读权限(readonly)
- ✅ `GET /api/data/:tag`
- ✅ `GET /api/data`
- ❌ `POST /api/data`
- ❌ `DELETE /api/data/:tag`
#### 读写权限(readwrite)
- ✅ 所有操作
#### 权限验证失败响应
```json
{
"error": "Permission denied",
"message": "This token has readonly permission",
"required": "readwrite"
}
```
### 3.4 安全机制
#### 1. 限流(Rate Limiting)
- **规则**:每个 Token 每分钟最多 60 次请求
- **超出后响应**:
```json
{
"error": "Rate limit exceeded",
"message": "Maximum 60 requests per minute",
"retry_after": 30
}
```
- **状态码**:429
#### 2. 失败次数限制
- **规则**:连续失败 5 次自动禁用 Token
- **失败场景**:
- Token 不存在
- Token 已过期
- Token 已禁用
- **自动禁用后响应**:
```json
{
"error": "Token disabled",
"message": "Token has been disabled due to too many failed attempts",
"failed_attempts": 5
}
```
#### 3. 过期检测
- 每次请求时检查 `expires_at`
- 过期 Token 自动标记为 `expired` 状态
- 过期响应:
```json
{
"error": "Token expired",
"expires_at": "2026-09-10 10:30:00"
}
```
#### 4. 双因素验证(敏感操作)
敏感操作需要同时提供 Token 和主密码:
- 删除 Token
- 修改 Token 权限(从 readonly 改为 readwrite)
### 3.5 数据存储
#### 文件结构
```
/data/
tokens.json # Token 索引和基本信息
tokens/
dss_a1b2c3...json # 单个 Token 的完整数据
```
#### tokens.json 格式
```json
{
"tokens": {
"dss_a1b2c3d4e5f6789012345678901234ab": {
"name": "生产服务器A",
"permission": "readonly",
"status": "active",
"created_at": "2026-09-13 10:30:00",
"expires_at": null,
"file": "tokens/dss_a1b2c3d4e5f6789012345678901234ab.json"
}
}
}
```
#### 单个 Token 文件格式
```json
{
"token": "dss_a1b2c3d4e5f6789012345678901234ab",
"name": "生产服务器A",
"permission": "readonly",
"status": "active",
"created_at": "2026-09-13 10:30:00",
"expires_at": null,
"stats": {
"total_requests": 1523,
"last_used_at": "2026-09-13 15:20:10",
"last_ip": "192.168.1.100",
"failed_attempts": 0
},
"access_log": [
{
"timestamp": "2026-09-13 15:20:10",
"ip": "192.168.1.100",
"method": "GET",
"path": "/api/data/weibo:ykaayk",
"status": 200
}
]
}
```
## 4. Web 界面需求
### 4.1 Token 管理页面
#### 入口
在现有 Web 管理界面(`http://124.220.209.75:5001/`)顶部导航栏添加:
```
[数据管理] [Token 管理]
```
#### Token 列表页
**布局**:
```
+----------------------------------------------------------+
| Token 管理 [+ 创建 Token] |
+----------------------------------------------------------+
| 搜索: [__________] 状态: [全部 ▼] 权限: [全部 ▼] |
+----------------------------------------------------------+
| 名称 | Token (前12位) | 权限 | 状态 | 最后使用 | 操作 |
|----------------|---------------|-------|------|-----------|------------|
| 生产服务器A | dss_a1b2c3... | 只读 | 启用 | 5分钟前 | [详情][禁用][删除] |
| 测试脚本 | dss_xyz789... | 读写 | 禁用 | 2天前 | [详情][启用][删除] |
+----------------------------------------------------------+
```
**功能**:
- 表格展示所有 Token
- 搜索框(按名称过滤)
- 下拉筛选(状态、权限)
- 快速操作按钮(详情、启用/禁用、删除)
- Token 列显示前 12 位,点击可复制完整 Token
#### 创建 Token 弹框
**触发**:点击 [+ 创建 Token] 按钮
**弹框内容**:
```
+-----------------------------------------------+
| 创建 Token |
+-----------------------------------------------+
| Token 名称 * |
| [_______________________________________] |
| 用于识别客户端,如"生产服务器A" |
| |
| 权限范围 * |
| ○ 只读(仅查询数据) |
| ● 读写(可上传、删除数据) |
| |
| 过期设置 * |
| ● 永久有效 |
| ○ 30天后过期 |
| ○ 90天后过期 |
| ○ 自定义: [___] 天 |
| |
| [取消] [创建] |
+-----------------------------------------------+
```
**验证规则**:
- Token 名称:必填,1-50 字符
- 权限范围:必选
- 过期设置:必选
**成功后**:
- 关闭弹框
- 显示新创建的 Token 详情弹框(包含完整 Token,提示复制保存)
- 刷新列表
#### Token 详情弹框
**触发**:
1. 点击列表中的 [详情] 按钮
2. 创建 Token 成功后自动弹出
**弹框内容**:
```
+-----------------------------------------------+
| Token 详情 |
+-----------------------------------------------+
| Token 值 |
| [••••••••••••••••••••••••••••] [👁 显示] [📋 复制] |
| |
| 基本信息 |
| 名称: 生产服务器A |
| 权限: 只读 |
| 状态: 🟢 启用中 |
| 创建时间: 2026-09-13 10:30:00 |
| 过期时间: 永久有效 |
| |
| 使用统计 |
| 总请求数: 1,523 次 |
| 最后使用: 2026-09-13 15:20:10 (5分钟前) |
| 最后 IP: 192.168.1.100 |
| 失败次数: 0 |
| |
| 访问日志 (最近 20 条) |
| +-------------------------------------------+ |
| | 时间 | IP | 操作 | 状态 | |
| |------------------|--------------|---------|-----| |
| | 15:20:10 | 192.168.1.100| GET ...| 200 | |
| | 15:15:05 | 192.168.1.100| GET ...| 200 | |
| +-------------------------------------------+ |
| |
| [关闭] [修改] [删除] |
+-----------------------------------------------+
```
**功能**:
- Token 值默认遮罩,点击 [👁 显示] 切换显示/隐藏
- [📋 复制] 按钮复制完整 Token
- 状态图标:🟢 启用中 / 🔴 已禁用 / ⏰ 已过期
- 访问日志表格可滚动
- [修改] 按钮打开编辑弹框
- [删除] 按钮触发删除确认
#### 编辑 Token 弹框
**触发**:点击详情弹框中的 [修改] 按钮
**弹框内容**:
```
+-----------------------------------------------+
| 编辑 Token |
+-----------------------------------------------+
| Token 名称 * |
| [生产服务器A_____________________________] |
| |
| 状态 |
| ● 启用 |
| ○ 禁用 |
| |
| [取消] [保存] |
+-----------------------------------------------+
```
**说明**:
- 权限和过期时间创建后不可修改
- 只能修改名称和状态
#### 删除 Token 确认弹框
**触发**:点击 [删除] 按钮
**弹框内容**:
```
+-----------------------------------------------+
| ⚠️ 删除 Token |
+-----------------------------------------------+
| 确认要删除以下 Token 吗? |
| |
| 名称: 生产服务器A |
| Token: dss_a1b2c3... (前12位) |
| |
| ⚠️ 此操作不可恢复,使用此 Token 的客户端将无法访问 |
| |
| 输入主密码以确认: |
| [_______________________________________] |
| |
| [取消] [确认删除] |
+-----------------------------------------------+
```
**验证**:
- 必须输入正确的主密码(`admin123123123`)
- 密码错误显示红色提示:"密码错误,请重试"
### 4.2 UI/UX 规范
#### 通知提示
- **成功**:绿色,5 秒自动关闭
- "✅ Token 创建成功"
- "✅ Token 已禁用"
- "✅ Token 已删除"
- **错误**:红色,15 秒自动关闭
- "❌ Token 名称不能为空"
- "❌ 主密码错误"
- "❌ Token 不存在"
- **信息**:蓝色,8 秒自动关闭
- "📋 Token 已复制到剪贴板"
#### 表单验证
- 实时验证(输入时)
- 错误时红框 + 聚焦
- 验证失败不关闭弹框
#### 异步操作反馈
- 按钮状态:`[创建]` → `[创建中...]` + 禁用
- 操作开始时显示通知:"正在创建 Token..."
- 完成后更新状态和通知
#### 颜色规范
- 主色:`#667eea`(紫色)
- 成功:`#10b981`(绿色)
- 警告:`#f59e0b`(橙色)
- 错误:`#ef4444`(红色)
- 信息:`#3b82f6`(蓝色)
## 5. 实现技术栈
### 后端
- **语言**:Lua (OpenResty)
- **存储**:JSON 文件系统
- **配置文件**:`nginx/nginx.conf`(需要修改)
### 前端
- **框架**:纯 HTML/CSS/JavaScript(无框架依赖)
- **样式**:参考现有 `web/index.html` 风格
- **HTTP 客户端**:Fetch API
## 6. 实现步骤建议
### Phase 1: 后端 Token 验证逻辑
1. 在 `nginx.conf` 中添加 Token 验证函数
2. 修改现有 API 路由,增加 `X-Token` 认证支持
3. 实现权限检查(readonly vs readwrite)
4. 保留 `X-Password` 向后兼容
### Phase 2: Token 管理 API
1. 实现 `POST /api/tokens` 创建 Token
2. 实现 `GET /api/tokens` 列出所有 Token
3. 实现 `GET /api/tokens/:token` 获取详情
4. 实现 `PATCH /api/tokens/:token` 更新状态
5. 实现 `DELETE /api/tokens/:token` 删除 Token
### Phase 3: 审计和安全机制
1. 记录每次 Token 使用(时间、IP、操作)
2. 实现限流逻辑(60次/分钟)
3. 实现失败次数统计和自动禁用
4. 实现过期检测
### Phase 4: Web 界面
1. 在现有 `web/index.html` 顶部添加导航切换
2. 创建 Token 管理页面(列表 + 搜索过滤)
3. 实现创建 Token 弹框
4. 实现 Token 详情弹框
5. 实现编辑和删除功能
### Phase 5: 测试和文档
1. 手动测试所有 API 端点
2. 验证权限控制和安全机制
3. 更新 README.md 文档
4. 提供 Token 使用示例
## 7. 向后兼容性
### 现有客户端
- 使用 `X-Password: admin123123123` 的现有脚本和工具继续正常工作
- 无需立即迁移到 Token 机制
### 迁移路径
1. 部署新版本(增加 Token 功能)
2. 在 Web 界面创建新 Token
3. 逐步更新客户端配置(从 `X-Password` 改为 `X-Token`)
4. 最终可选择性禁用 `X-Password`(配置开关)
## 8. 测试用例
### API 测试
```bash
# 1. 创建 Token
curl -X POST http://localhost:5001/api/tokens \
-H "X-Password: admin123123123" \
-H "Content-Type: application/json" \
-d '{"name":"测试Token","permission":"readonly","expires_in":null}'
# 2. 使用 Token 获取数据
curl http://localhost:5001/api/data/weibo:ykaayk \
-H "X-Token: dss_xxxxx"
# 3. 测试只读权限(应该失败)
curl -X POST http://localhost:5001/api/data \
-H "X-Token: dss_xxxxx" \
-H "Content-Type: application/json" \
-d '{"tag":"test","type":"token","data":{}}'
# 4. 列出所有 Token
curl http://localhost:5001/api/tokens \
-H "X-Password: admin123123123"
# 5. 禁用 Token
curl -X PATCH http://localhost:5001/api/tokens/dss_xxxxx \
-H "X-Password: admin123123123" \
-H "Content-Type: application/json" \
-d '{"status":"disabled"}'
# 6. 删除 Token
curl -X DELETE http://localhost:5001/api/tokens/dss_xxxxx \
-H "X-Password: admin123123123" \
-H "Content-Type: application/json" \
-d '{"confirm_password":"admin123123123"}'
```
## 9. 验收标准
### 功能验收
- ✅ 可以通过 Web 界面创建 Token
- ✅ 创建后显示完整 Token 值(一次性展示)
- ✅ Token 列表正确显示所有 Token 及其状态
- ✅ 只读 Token 无法执行写操作
- ✅ 读写 Token 可以执行所有操作
- ✅ 访问日志正确记录(时间、IP、操作、状态)
- ✅ 限流生效(60次/分钟)
- ✅ 连续失败 5 次自动禁用
- ✅ 过期 Token 无法使用
- ✅ 删除 Token 需要二次确认
- ✅ 旧的 `X-Password` 认证继续工作
### UI/UX 验收
- ✅ 所有弹框居中显示,带遮罩层
- ✅ 表单验证实时生效,红框 + 聚焦
- ✅ 异步操作有加载状态和通知
- ✅ 通知自动关闭(成功5秒,错误15秒)
- ✅ Token 值支持显示/隐藏切换
- ✅ 点击复制成功后有反馈
- ✅ 响应式布局,移动端友好
### 安全验收
- ✅ Token 存储在服务器端,前端只展示一次
- ✅ 主密码验证正确(创建、删除时)
- ✅ 限流正确拦截过多请求
- ✅ 失败次数统计准确
- ✅ 权限控制无漏洞
## 10. 附录:错误码对照表
| 状态码 | 错误信息 | 场景 |
|--------|---------|------|
| 200 | - | 请求成功 |
| 400 | Invalid request | 请求参数错误 |
| 401 | Invalid token | Token 不存在或无效 |
| 401 | Token disabled | Token 已被禁用 |
| 401 | Token expired | Token 已过期 |
| 403 | Permission denied | 权限不足(只读 Token 尝试写操作)|
| 404 | Token not found | Token 不存在 |
| 429 | Rate limit exceeded | 超过限流限制 |
| 500 | Internal server error | 服务器内部错误 |
---
**文档版本**:v1.0
**创建日期**:2026-09-13
**作者**:Hermes AI Agent
**项目路径**:`/home/hermes/projects/data-sync-service`