多库检索与存储修复: - RRF 融合去重改用 (collection, chunk_id) 复合键,修复同名文件结果被吞 - DocStore 存储路径加 collection 前缀,修复跨库同名切片数据覆盖 - search_multiple 去重改用复合键 - chunk_id 解析改用 rsplit 兼容下划线文件名 上传与版本管理修复: - 同名文件上传改为覆盖模式,自动清理旧切片 - 修复首次上传不创建版本记录 - 修复覆盖上传版本号回退到 v1 - sync ADDED 分支改用动态版本号生成 - _generate_version_id 改为基于全部版本递增 - 废止/恢复操作同步 SQLite 版本记录 - mark_document_as_superseded 改为仅更新 SQLite 删除清理修复: - 删除文档时同步清理 SQLite 版本记录和变更日志 - 删除向量库时同步清理该库所有版本记录 - cleanup 改为清理 SQLite 记录而非 ChromaDB 测试: - test_version_management.py: 27 条版本管理单元测试 - test_edge_cases.py: 28 条边界用例测试 - test_upload_dedup.py: 5 条上传去重测试 - e2e_risk_test.py: 27 条端到端风险测试 文档: - 新增风险边界问题修复注意事项.md(面向后端的对接文档) - 新增向量库边界风险分析.md - 更新多篇现有文档
411 lines
11 KiB
Markdown
411 lines
11 KiB
Markdown
# 认证与权限配置指南
|
||
|
||
> **文档类型**: 配置指南
|
||
> **创建日期**: 2026-04-12
|
||
> **最后更新**: 2026-06-04
|
||
> **状态**: 已实施
|
||
|
||
---
|
||
|
||
## 一、概述
|
||
|
||
API 服务采用网关注入认证模式,从 HTTP Header 中读取用户信息,不处理本地登录认证。
|
||
|
||
### 1.1 认证流程
|
||
|
||
```
|
||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||
│ 前端 │────▶│ 网关 │────▶│ 后端 API │────▶│ Dify │
|
||
│ (JWT Token) │ │ (验证注入) │ │ (Header认证) │ │ (Token认证) │
|
||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||
│ │ │ │
|
||
│ Authorization: │ X-User-ID: xxx │ │
|
||
│ Bearer <token> │ X-User-Role: xxx │ user_token: xxx │
|
||
└──────────────────▶└──────────────────▶└──────────────────▶│
|
||
│ │
|
||
│ Authorization: Bearer {{user_token}}
|
||
```
|
||
|
||
---
|
||
|
||
## 二、网关配置
|
||
|
||
### 2.1 需要注入的 Header
|
||
|
||
| Header 名称 | 必需 | 说明 | 示例 |
|
||
|------------|------|------|------|
|
||
| X-User-ID | 是 | 用户唯一标识 | user001 |
|
||
| X-User-Name | 否 | 用户名 | 张三 |
|
||
| X-User-Role | 否 | 用户角色 | admin/manager/user |
|
||
| X-User-Department | 否 | 部门 | 技术部 |
|
||
|
||
### 2.2 前端配置
|
||
|
||
- 用户登录后获取 JWT token
|
||
- 请求时在 `Authorization` Header 中携带 token:`Bearer <token>`
|
||
|
||
### 2.3 后端配置
|
||
|
||
- 网关验证 token 后注入用户信息 Header
|
||
- 后端 API 通过 `request.headers.get('X-User-ID')` 获取用户信息
|
||
|
||
---
|
||
|
||
## 三、角色权限
|
||
|
||
### 3.1 角色映射
|
||
|
||
后端角色会自动映射到本地角色:
|
||
|
||
| 后端角色 | 本地角色 |
|
||
|---------|---------|
|
||
| administrator | admin |
|
||
| admin | admin |
|
||
| manager | manager |
|
||
| user | user |
|
||
| normal | user |
|
||
| guest | user |
|
||
|
||
如需添加新的角色映射,请修改 `auth/gateway.py` 中的 `ROLE_MAPPING` 字典。
|
||
|
||
### 3.2 权限级别
|
||
|
||
| 角色 | 可访问的安全级别 | 向量库读取 | 向量库写入 | 向量库删除 | 同步 |
|
||
|------|------------------|------------|------------|------------|------|
|
||
| admin | public, internal, confidential | 所有 | 所有 | 所有 | 所有 |
|
||
| manager | public, internal, confidential | public + 本部门 | 本部门 | 本部门 | 本部门 |
|
||
| user | public, internal | public + 本部门 | ❌ | ❌ | ❌ |
|
||
|
||
---
|
||
|
||
## 四、开发测试
|
||
|
||
### 4.1 启用开发模式
|
||
|
||
```bash
|
||
# Windows
|
||
set DEV_MODE=true
|
||
|
||
# Linux/Mac
|
||
export DEV_MODE=true
|
||
```
|
||
|
||
### 4.2 模拟用户测试
|
||
|
||
开发模式下,可以:
|
||
1. 不传 Header 会自动使用默认测试用户(admin/开发部)
|
||
2. 使用 `Authorization: Bearer mock-token-<username>` 模拟登录
|
||
|
||
**测试账号**:
|
||
| 用户名 | 密码 | 角色 | 部门 |
|
||
|--------|------|------|------|
|
||
| admin | admin123 | admin | 管理部 |
|
||
| testuser | test123 | user | 技术部 |
|
||
| manager | manager123 | manager | 财务部 |
|
||
|
||
### 4.3 使用 Header 模拟用户
|
||
|
||
```bash
|
||
# 测试普通用户
|
||
curl -H "X-User-ID: test001" -H "X-User-Name: 测试用户" -H "X-User-Role: user" \
|
||
http://localhost:5001/auth/me
|
||
|
||
# 测试管理员
|
||
curl -H "X-User-ID: admin001" -H "X-User-Name: 管理员" -H "X-User-Role: admin" \
|
||
http://localhost:5001/stats
|
||
|
||
# 使用 mock token
|
||
curl -H "Authorization: Bearer mock-token-admin" \
|
||
http://localhost:5001/auth/me
|
||
```
|
||
|
||
---
|
||
|
||
## 五、API 接口分类
|
||
|
||
### 5.1 公开接口(无需认证)
|
||
|
||
| 接口 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/health` | GET | 健康检查 |
|
||
|
||
### 5.2 需要认证的接口
|
||
|
||
所有其他接口都需要通过网关访问,网关会注入用户信息。
|
||
|
||
### 5.3 管理员专用接口
|
||
|
||
以下接口需要 `admin` 角色:
|
||
|
||
| 接口 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/stats` | GET | 系统统计 |
|
||
| `/sync` | POST | 手动触发同步 |
|
||
| `/sync/start` | POST | 启动文件监控 |
|
||
| `/sync/stop` | POST | 停止文件监控 |
|
||
| `/collections` | POST | 创建向量库 |
|
||
| `/collections/<name>` | DELETE | 删除向量库 |
|
||
| `/faq` | POST | 新增 FAQ |
|
||
| `/faq/<id>` | PUT/DELETE | 更新/删除 FAQ |
|
||
| `/exam/<id>/review` | POST | 审核试卷 |
|
||
|
||
### 5.4 保留的接口
|
||
|
||
| 接口 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/auth/me` | GET | 获取当前用户信息(从 Header 读取) |
|
||
|
||
### 5.5 已移除的接口
|
||
|
||
以下接口已移除(由后端网关处理):
|
||
- `POST /auth/login` - 用户登录
|
||
- `POST /auth/register` - 用户注册
|
||
- `POST /auth/change-password` - 修改密码
|
||
- `GET /auth/users` - 用户列表
|
||
- `PUT /auth/users/<user_id>` - 更新用户
|
||
- `DELETE /auth/users/<user_id>` - 删除用户
|
||
|
||
---
|
||
|
||
## 六、错误响应
|
||
|
||
### 6.1 401 未认证
|
||
|
||
```json
|
||
{
|
||
"error": "缺少用户信息",
|
||
"message": "请通过网关访问,或设置 DEV_MODE=true 进行开发测试"
|
||
}
|
||
```
|
||
|
||
### 6.2 403 权限不足
|
||
|
||
```json
|
||
{
|
||
"error": "权限不足",
|
||
"message": "此接口需要以下角色之一: admin",
|
||
"your_role": "user"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 七、对接清单
|
||
|
||
### 7.1 Dify 工作流修改
|
||
|
||
**当前状态**:使用变量传入用户信息
|
||
```yaml
|
||
variables:
|
||
- variable: user_id
|
||
default: 'exam-system'
|
||
- variable: user_role
|
||
default: 'admin'
|
||
- variable: user_department
|
||
default: ''
|
||
|
||
headers: 'Content-Type:application/json
|
||
X-User-ID:{{#1774596726631.user_id#}}
|
||
X-User-Role:{{#1774596726631.user_role#}}
|
||
X-User-Department:{{#1774596726631.user_department#}}'
|
||
```
|
||
|
||
**对接后修改**:改用 Bearer token 认证
|
||
```yaml
|
||
variables:
|
||
- variable: user_token
|
||
label: 用户Token
|
||
hint: 'JWT认证token,由网关注入'
|
||
default: ''
|
||
required: true
|
||
type: text-input
|
||
|
||
headers: 'Content-Type:application/json
|
||
Authorization:Bearer {{#1774596726631.user_token#}}'
|
||
```
|
||
|
||
**操作步骤**:
|
||
1. 移除 `user_id`、`user_role`、`user_department` 变量
|
||
2. 添加 `user_token` 变量
|
||
3. 修改 HTTP 请求的 headers,使用 `Authorization: Bearer <token>`
|
||
|
||
### 7.2 Python 后端修改
|
||
|
||
**exam_pkg/generator.py 当前状态**:
|
||
```python
|
||
def generate_exam_by_file(
|
||
file_path: str,
|
||
collection: str,
|
||
user_id: str = None,
|
||
user_role: str = None,
|
||
user_department: str = None,
|
||
...
|
||
) -> dict:
|
||
inputs = {
|
||
"file_path": file_path,
|
||
"collection": collection,
|
||
"user_id": user_id or "exam-system",
|
||
"user_role": user_role or "admin",
|
||
"user_department": user_department or "",
|
||
...
|
||
}
|
||
```
|
||
|
||
**对接后修改**:
|
||
```python
|
||
def generate_exam_by_file(
|
||
file_path: str,
|
||
collection: str,
|
||
user_token: str = None, # 改为接收 token
|
||
...
|
||
) -> dict:
|
||
inputs = {
|
||
"file_path": file_path,
|
||
"collection": collection,
|
||
"user_token": user_token or "", # 传入 token
|
||
...
|
||
}
|
||
```
|
||
|
||
**exam_pkg/api.py 当前状态**:
|
||
```python
|
||
result = generate_exam_by_file(
|
||
file_path=file_path,
|
||
collection=collection,
|
||
user_id=user.get('user_id'),
|
||
user_role=user.get('role'),
|
||
user_department=user.get('department'),
|
||
...
|
||
)
|
||
```
|
||
|
||
**对接后修改**:
|
||
```python
|
||
# 从请求头获取 Authorization token
|
||
auth_header = request.headers.get('Authorization', '')
|
||
user_token = auth_header.replace('Bearer ', '') if auth_header.startswith('Bearer ') else ''
|
||
|
||
result = generate_exam_by_file(
|
||
file_path=file_path,
|
||
collection=collection,
|
||
user_token=user_token, # 传递 token
|
||
...
|
||
)
|
||
```
|
||
|
||
### 7.3 auth/gateway.py
|
||
|
||
**当前状态**:已支持网关注入 Header 认证,**无需修改**
|
||
|
||
```python
|
||
def require_gateway_auth(f):
|
||
# 从 Header 读取用户信息
|
||
user_id = request.headers.get('X-User-ID')
|
||
username = request.headers.get('X-User-Name', '')
|
||
role = request.headers.get('X-User-Role', '')
|
||
department = request.headers.get('X-User-Department', '')
|
||
|
||
# 必须有用户 ID
|
||
if not user_id:
|
||
return jsonify({"error": "缺少用户信息"}), 401
|
||
|
||
# 将用户信息附加到 request 对象
|
||
request.current_user = {...}
|
||
```
|
||
|
||
---
|
||
|
||
## 八、测试验证
|
||
|
||
### 8.1 网关认证测试
|
||
|
||
```bash
|
||
# 测试网关注入 Header
|
||
curl -X POST http://localhost:5001/search \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-User-ID: test_user" \
|
||
-H "X-User-Role: admin" \
|
||
-H "X-User-Department: finance" \
|
||
-d '{"query": "测试", "top_k": 3}'
|
||
```
|
||
|
||
**预期结果**:返回搜索结果,无认证错误
|
||
|
||
### 8.2 出题功能测试
|
||
|
||
```bash
|
||
# 测试出题接口(携带网关注入的 Header)
|
||
curl -X POST http://localhost:5001/exam/generate-by-file \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-User-ID: test_user" \
|
||
-H "X-User-Role: admin" \
|
||
-H "Authorization: Bearer <JWT_TOKEN>" \
|
||
-d '{
|
||
"file_path": "public/公司简介.txt",
|
||
"collection": "public_kb",
|
||
"choice_count": 2
|
||
}'
|
||
```
|
||
|
||
**预期结果**:成功生成题目 JSON
|
||
|
||
### 8.3 权限控制测试
|
||
|
||
| 用户角色 | 可访问向量库 | 预期结果 |
|
||
|---------|-------------|---------|
|
||
| admin | 所有向量库 | ✅ 成功 |
|
||
| manager (finance) | public_kb + dept_finance | ✅ 本部门成功,其他部门拒绝 |
|
||
| user (hr) | public_kb + dept_hr(只读) | ✅ 只读成功,写入拒绝 |
|
||
|
||
---
|
||
|
||
## 九、回滚方案
|
||
|
||
如果对接后出现问题,可以快速回滚到模拟测试模式:
|
||
|
||
### 9.1 设置环境变量
|
||
|
||
```bash
|
||
# Windows
|
||
set DEV_MODE=true
|
||
|
||
# Linux/Mac
|
||
export DEV_MODE=true
|
||
```
|
||
|
||
### 9.2 恢复工作流
|
||
|
||
使用 Git 恢复工作流文件:
|
||
```bash
|
||
git checkout tests/自动出题(带溯源).yml
|
||
git checkout tests/自动批卷\(带溯源\)\ .yml
|
||
```
|
||
|
||
---
|
||
|
||
## 十、文件变更记录
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| auth/gateway.py | 新增,网关认证模块 |
|
||
| exam_pkg/api.py | 更新认证装饰器 |
|
||
|
||
---
|
||
|
||
## 十一、注意事项
|
||
|
||
1. **角色映射**:如果后端的角色名称与本地不同,需要修改 `auth/gateway.py` 中的 `ROLE_MAPPING`
|
||
2. **开发模式**:生产环境请确保不设置 `DEV_MODE=true`
|
||
3. **前端**:前端由其他团队负责,需要他们确保请求经过网关
|
||
4. **安全性**:敏感操作需要 admin 角色,确保权限控制正确
|
||
|
||
---
|
||
|
||
## 十二、变更记录
|
||
|
||
| 日期 | 版本 | 变更内容 |
|
||
|------|------|---------|
|
||
| 2026-06-04 | 2.1 | 移除已废弃的 Graph RAG 端点(/graph/build)和审计端点(/audit/logs);移除 api/auth_routes.py 引用;代码示例更新为 generator.py |
|
||
| 2026-04-13 | 2.0 | 合并登录功能对接清单和网关认证对接说明 |
|
||
| 2026-04-12 | 1.0 | 创建认证对接文档 |
|