整理项目文档并优化权限监控与安装脚本

This commit is contained in:
Cheng Zhou
2026-05-21 11:39:00 +08:00
parent 6d682103f3
commit 9305ced664
31 changed files with 508 additions and 4083 deletions
-283
View File
@@ -1,283 +0,0 @@
# 接口级权限系统 - 实现总结
## 项目概述
本项目实现了从模块级权限到接口级权限的系统迁移,支持更细粒度的API端点级权限控制,同时保持向后兼容性。
## 实现阶段
### 第1-6阶段:核心基础设施(已完成)
#### 1. 数据库模型
- **ApiEndpointPermission**:存储接口级权限配置
- **ApiEndpointRegistry**:注册系统中所有API端点
#### 2. 权限配置系统
- **api_permissions.py**:中央权限配置注册表
- **MODULE_TO_ENDPOINTS**:向后兼容映射表
#### 3. 权限检查函数
- **role_has_api_permission()**:检查角色是否有权访问特定接口
- **get_api_endpoint_permissions()**:获取项目权限矩阵
- **replace_api_endpoint_permissions()**:更新项目权限矩阵
#### 4. FastAPI 集成
- **require_api_permission()**:依赖注入函数
- **@register_api_endpoint**:装饰器注册端点
#### 5. 权限管理API
- **GET /studies/{study_id}/api-permissions**:获取权限矩阵
- **PUT /studies/{study_id}/api-permissions**:更新权限矩阵
### 第8阶段:迁移第3批模块(已完成)
#### 迁移的模块
**第一优先级(关键业务)**
- startup.py:19个端点(伦理审批、可行性评估、预算、时间表)
- project_permissions.py2个端点(项目权限查询、更新)
- overview.py1个端点(项目概览)
**第二优先级(重要业务)**
- monitoring_visit_issues.py7个端点(监查问题管理)
- drug_shipments.py5个端点(药物发货管理)
- material_equipments.py5个端点(物资管理)
- subject_pds.py4个端点(参与者PDS
- audit_logs.py3个端点(审计日志)
**第三优先级(辅助功能)**
- visits.py5个端点(访视管理)
- knowledge_notes.py5个端点(知识库笔记)
- subject_histories.py5个端点(参与者历史)
- project_milestones.py2个端点(项目里程碑)
#### 迁移统计
- **迁移端点数**63 个
- **涉及文件**12 个
- **新增测试**34 个
- **总测试数**:109 个(包括前7阶段的75个)
## 权限配置示例
### Members 模块权限矩阵
| 角色 | 添加成员 | 查询成员 | 更新成员 | 删除成员 | 查询候选人 |
|------|---------|---------|---------|---------|-----------|
| ADMIN | ✅ | ✅ | ✅ | ✅ | ✅ |
| PM | ✅ | ✅ | ✅ | ✅ | ✅ |
| CRA | ❌ | ❌ | ❌ | ❌ | ❌ |
| PV | ❌ | ❌ | ❌ | ❌ | ❌ |
| MEDICAL_REVIEW | ❌ | ❌ | ❌ | ❌ | ❌ |
| IMP | ❌ | ❌ | ❌ | ❌ | ❌ |
| QA | ❌ | ❌ | ❌ | ❌ | ❌ |
### Sites 模块权限矩阵
| 角色 | 创建中心 | 查询中心 | 更新中心 | 删除中心 |
|------|---------|---------|---------|---------|
| ADMIN | ✅ | ✅ | ✅ | ✅ |
| PM | ✅ | ✅ | ✅ | ✅ |
| CRA | ❌ | ❌ | ❌ | ❌ |
| PV | ❌ | ❌ | ❌ | ❌ |
| MEDICAL_REVIEW | ❌ | ❌ | ❌ | ❌ |
| IMP | ❌ | ❌ | ❌ | ❌ |
| QA | ❌ | ❌ | ❌ | ❌ |
## 迁移代码示例
### 迁移前(模块级权限)
```python
@router.post(
"/",
response_model=StudyMemberRead,
dependencies=[
Depends(require_study_permission("project_members", "write")),
],
)
async def add_member(...):
pass
```
### 迁移后(接口级权限)
```python
@router.post(
"/",
response_model=StudyMemberRead,
dependencies=[
Depends(require_api_permission("POST:/studies/{study_id}/members")),
Depends(require_study_not_locked())
],
)
@register_api_endpoint(
endpoint_key="POST:/studies/{study_id}/members",
module="project_members",
action="write",
description="添加项目成员",
default_roles=["PM"],
)
async def add_member(...):
pass
```
## 权限检查流程
```
请求到达
FastAPI依赖注入 → require_api_permission("POST:/studies/{study_id}/members")
role_has_api_permission(db, study_id, role, "POST:/studies/{study_id}/members")
├─ 查询 ApiEndpointPermission 表
│ ├─ 找到 → 返回 allowed 值
│ └─ 未找到 → 继续
└─ 回退到模块级权限
└─ role_has_project_permission(db, study_id, role, "project_members", "write")
├─ 查询 StudyRolePermission 表
└─ 返回权限结果
权限检查通过 → 执行业务逻辑
权限检查失败 → 返回 403 Forbidden
```
## 已迁移端点总览
### 第1批(22个端点)
- **subjects**5 个端点
- **risk_issues**3 个端点
- **fees**8 个端点
- **finance_contracts**5 个端点
### 第2批(9个端点)
- **members**5 个端点
- **sites**4 个端点
### 第3批(63个端点)
- **startup**19 个端点
- **project_permissions**2 个端点
- **overview**1 个端点
- **monitoring_visit_issues**7 个端点
- **drug_shipments**5 个端点
- **material_equipments**5 个端点
- **subject_pds**4 个端点
- **audit_logs**3 个端点
- **visits**5 个端点
- **knowledge_notes**5 个端点
- **subject_histories**5 个端点
- **project_milestones**2 个端点
### 总计:94 个端点
## 向后兼容性
系统支持两种权限检查方式的并行运行:
1. **接口级权限**(优先级高)
- 存储在 `ApiEndpointPermission`
- 支持细粒度的端点级控制
2. **模块级权限**(优先级低)
- 存储在 `StudyRolePermission`
- 用于未迁移的端点和向后兼容
**优先级规则**
- 如果存在接口级权限配置,使用接口级权限
- 如果不存在接口级权限配置,回退到模块级权限
- 如果两者都不存在,拒绝访问
## 测试覆盖
### 测试文件
- `test_api_permissions.py`12 个测试
- `test_api_permissions_endpoints.py`11 个测试
- `test_api_permissions_config.py`13 个测试
- `test_migrated_endpoints.py`22 个测试(第1批)
- `test_migrated_endpoints_batch2.py`17 个测试(第2批)
- `test_migrated_endpoints_batch3.py`34 个测试(第3批)
### 测试场景
- ✅ 接口级权限允许/拒绝
- ✅ 模块级权限回退
- ✅ 权限优先级验证
- ✅ 权限矩阵操作
- ✅ 向后兼容性验证
- ✅ 权限隔离验证
- ✅ 权限配置验证
### 覆盖率
- **代码覆盖率**85%
- **测试通过率**100%109/109
## 关键文件清单
### 新增文件
| 文件 | 用途 |
|------|------|
| `app/models/api_endpoint_permission.py` | 接口级权限模型 |
| `app/models/api_endpoint_registry.py` | 接口注册表模型 |
| `app/core/api_permissions.py` | 接口权限配置 |
| `app/core/decorators.py` | 装饰器辅助函数 |
| `app/api/v1/api_permissions.py` | 权限管理API |
| `tests/test_api_permissions.py` | 权限检查测试 |
| `tests/test_api_permissions_endpoints.py` | 权限管理API测试 |
| `tests/test_migrated_endpoints.py` | 第1批端点测试 |
| `tests/test_migrated_endpoints_batch2.py` | 第2批端点测试 |
### 修改文件
| 文件 | 修改内容 |
|------|---------|
| `app/core/project_permissions.py` | 新增接口级权限检查函数 |
| `app/core/deps.py` | 新增 `require_api_permission()` 函数 |
| `app/api/v1/subjects.py` | 迁移到接口级权限 |
| `app/api/v1/aes.py` | 迁移到接口级权限 |
| `app/api/v1/fees_contracts.py` | 迁移到接口级权限 |
| `app/api/v1/members.py` | 迁移到接口级权限(第2批) |
| `app/api/v1/sites.py` | 迁移到接口级权限(第2批) |
## 性能指标
- **测试执行时间**0.40 秒
- **平均单个测试时间**:3.7 毫秒
- **代码覆盖率**85%
- **总测试数**109 个
## 下一步工作
### 第9阶段:安全审计与性能优化(已完成)
**目标模块**
- ✅ 安全审计:检查权限系统的安全性
- ✅ 性能优化:优化权限检查的性能
- ✅ 缓存策略:实现权限缓存
**完成情况**
- ✅ 权限检查覆盖率 100%(94个端点全部受保护)
- ✅ 权限配置完整性优秀(101个端点完整配置)
- ✅ ADMIN角色处理一致且安全
- ✅ 实现权限矩阵缓存和成员身份缓存
- ✅ 性能提升 50%+,缓存命中率 > 80%
- ✅ 新增47个测试用例(性能测试12个 + 安全测试20个 + 缓存测试15个)
**生成文档**
- ✅ SECURITY_AUDIT.md - 安全审计报告
- ✅ PERFORMANCE_OPTIMIZATION.md - 性能优化报告
### 第10-11阶段
- 性能测试
- 文档更新
## 总结
接口级权限系统已成功实现,包括:
- ✅ 核心基础设施(数据库、配置、检查函数)
- ✅ FastAPI 集成(依赖注入、装饰器)
- ✅ 权限管理API
- ✅ 第1批模块迁移(22 个端点)
- ✅ 第2批模块迁移(9 个端点)
- ✅ 第3批模块迁移(63 个端点)
- ✅ 全面的测试覆盖(109 个测试)
- ✅ 向后兼容性保证
**已迁移端点总数:94 个**
系统已准备好进行第9阶段的安全审计和性能优化。
-410
View File
@@ -1,410 +0,0 @@
# 权限系统监控仪表板
## 概述
权限系统监控仪表板提供了权限系统运行状态的实时可视化,包括权限检查性能、缓存效率、告警信息和系统健康状态。
---
## API 端点
### 1. 获取权限系统指标
**端点**: `GET /api/v1/permission-monitoring/metrics`
**描述**: 获取权限检查和缓存的运行指标
**响应示例**:
```json
{
"check_metrics": {
"total_checks": 1000,
"allowed_checks": 950,
"denied_checks": 50,
"total_time": 5.234,
"min_time": 0.001,
"max_time": 0.05,
"avg_time": 0.005,
"allow_rate": 95.0,
"deny_rate": 5.0,
"error_rate": 0.1,
"errors": 1
},
"cache_metrics": {
"total_accesses": 1000,
"cache_hits": 850,
"cache_misses": 150,
"cache_invalidations": 5,
"hit_rate": 85.0,
"miss_rate": 15.0
},
"uptime_seconds": 3600
}
```
**关键指标**:
- `total_checks`: 总权限检查次数
- `allowed_checks`: 允许的检查次数
- `denied_checks`: 拒绝的检查次数
- `avg_time`: 平均权限检查耗时(秒)
- `allow_rate`: 允许率(百分比)
- `cache_hits`: 缓存命中次数
- `hit_rate`: 缓存命中率(百分比)
---
### 2. 获取缓存统计
**端点**: `GET /api/v1/permission-monitoring/cache-stats`
**描述**: 获取缓存的详细统计信息
**响应示例**:
```json
{
"cache_items": {
"project_permissions_count": 10,
"member_role_count": 50,
"total_count": 60
},
"cache_metrics": {
"total_accesses": 1000,
"cache_hits": 850,
"cache_misses": 150,
"cache_invalidations": 5,
"hit_rate": 85.0,
"miss_rate": 15.0
}
}
```
**关键指标**:
- `project_permissions_count`: 项目权限缓存项目数
- `member_role_count`: 成员角色缓存项目数
- `hit_rate`: 缓存命中率
---
### 3. 获取告警列表
**端点**: `GET /api/v1/permission-monitoring/alerts`
**参数**:
- `level` (可选): 告警级别过滤 (info, warning, error)
- `limit` (可选): 返回的最大告警数,默认 100
**响应示例**:
```json
{
"total": 5,
"alerts": [
{
"timestamp": 1715692800.123,
"level": "warning",
"type": "slow_permission_check",
"message": "权限检查耗时过长: 52.34ms",
"data": {
"elapsed_time": 0.05234
}
},
{
"timestamp": 1715692799.456,
"level": "error",
"type": "permission_check_error",
"message": "权限检查出错: database connection timeout",
"data": {
"error": "database connection timeout"
}
}
]
}
```
**告警类型**:
- `slow_permission_check`: 权限检查耗时过长(>50ms)
- `permission_check_error`: 权限检查出错
---
### 4. 权限系统健康检查
**端点**: `GET /api/v1/permission-monitoring/health`
**描述**: 获取权限系统的健康状态
**响应示例**:
```json
{
"status": "healthy",
"health_score": 95,
"issues": [],
"metrics": {
"check_metrics": { ... },
"cache_metrics": { ... },
"uptime_seconds": 3600
},
"cache_stats": { ... }
}
```
**健康状态**:
- `healthy`: 健康(分数 >= 80
- `degraded`: 降级(分数 50-80
- `unhealthy`: 不健康(分数 < 50
**健康评分规则**:
- 初始分数: 100
- 错误率 > 1%: -20
- 缓存命中率 < 50%: -10
- 平均响应时间 > 10ms: -10
- 权限拒绝率 > 50%: -5
---
### 5. 重置指标
**端点**: `POST /api/v1/permission-monitoring/reset-metrics`
**描述**: 重置所有累积的指标数据
**响应示例**:
```json
{
"message": "指标已重置"
}
```
---
### 6. 清除告警
**端点**: `POST /api/v1/permission-monitoring/clear-alerts`
**描述**: 删除所有累积的告警记录
**响应示例**:
```json
{
"message": "告警已清除"
}
```
---
## 监控指标详解
### 权限检查指标
| 指标 | 说明 | 目标值 |
|------|------|--------|
| `total_checks` | 总权限检查次数 | - |
| `allowed_checks` | 允许的检查次数 | - |
| `denied_checks` | 拒绝的检查次数 | - |
| `allow_rate` | 允许率(百分比) | > 90% |
| `deny_rate` | 拒绝率(百分比) | < 10% |
| `avg_time` | 平均权限检查耗时 | < 10ms |
| `min_time` | 最小权限检查耗时 | - |
| `max_time` | 最大权限检查耗时 | < 100ms |
| `error_rate` | 错误率(百分比) | < 1% |
| `errors` | 错误次数 | 0 |
### 缓存指标
| 指标 | 说明 | 目标值 |
|------|------|--------|
| `total_accesses` | 总缓存访问次数 | - |
| `cache_hits` | 缓存命中次数 | - |
| `cache_misses` | 缓存未命中次数 | - |
| `hit_rate` | 缓存命中率(百分比) | > 80% |
| `miss_rate` | 缓存未命中率(百分比) | < 20% |
| `cache_invalidations` | 缓存失效次数 | - |
---
## 告警规则
### 性能告警
**慢速权限检查**
- 触发条件: 权限检查耗时 > 50ms
- 级别: warning
- 建议: 检查数据库性能或缓存配置
### 错误告警
**权限检查错误**
- 触发条件: 权限检查抛出异常
- 级别: error
- 建议: 检查错误日志,排查问题
---
## 监控最佳实践
### 1. 定期检查健康状态
```bash
# 每5分钟检查一次健康状态
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/health
```
### 2. 监控缓存命中率
```bash
# 检查缓存统计
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/cache-stats
```
**目标**: 缓存命中率 > 80%
### 3. 监控权限检查性能
```bash
# 获取权限系统指标
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/metrics
```
**目标**: 平均响应时间 < 10ms
### 4. 监控告警
```bash
# 获取最近的告警
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/v1/permission-monitoring/alerts?limit=20"
```
### 5. 定期重置指标
```bash
# 每天重置一次指标,用于日报
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/reset-metrics
```
---
## 故障排除
### 问题1: 缓存命中率低(< 50%)
**可能原因**:
1. 缓存 TTL 过短
2. 缓存失效频率过高
3. 权限变更频繁
**解决方案**:
1. 增加缓存 TTL(默认 5 分钟)
2. 检查权限变更频率
3. 优化权限更新逻辑
### 问题2: 权限检查响应时间长(> 50ms)
**可能原因**:
1. 数据库查询慢
2. 缓存未命中
3. 并发请求过多
**解决方案**:
1. 检查数据库性能
2. 增加缓存 TTL
3. 添加数据库索引
### 问题3: 权限检查错误率高(> 1%)
**可能原因**:
1. 数据库连接问题
2. 权限配置错误
3. 并发修改问题
**解决方案**:
1. 检查数据库连接
2. 验证权限配置
3. 检查并发修改逻辑
---
## 集成示例
### Python 客户端
```python
import requests
# 获取权限系统指标
response = requests.get(
"http://localhost:8000/api/v1/permission-monitoring/metrics",
headers={"Authorization": f"Bearer {token}"}
)
metrics = response.json()
# 检查缓存命中率
cache_hit_rate = metrics["cache_metrics"]["hit_rate"]
if cache_hit_rate < 80:
print(f"警告: 缓存命中率低 ({cache_hit_rate}%)")
# 检查平均响应时间
avg_time = metrics["check_metrics"]["avg_time"]
if avg_time > 0.01: # 10ms
print(f"警告: 权限检查响应时间长 ({avg_time*1000:.2f}ms)")
```
### JavaScript 客户端
```javascript
// 获取权限系统健康状态
async function checkPermissionHealth() {
const response = await fetch(
'http://localhost:8000/api/v1/permission-monitoring/health',
{
headers: {
'Authorization': `Bearer ${token}`
}
}
);
const health = await response.json();
console.log(`健康状态: ${health.status}`);
console.log(`健康分数: ${health.health_score}`);
if (health.issues.length > 0) {
console.warn('发现问题:', health.issues);
}
}
```
---
## 仪表板建议
### 实时监控仪表板
建议使用 Grafana 或类似的可视化工具创建实时监控仪表板,包括:
1. **权限检查性能**
- 平均响应时间趋势
- 允许/拒绝率
- 错误率
2. **缓存效率**
- 缓存命中率趋势
- 缓存项目数
- 缓存失效频率
3. **系统健康**
- 健康分数
- 告警数量
- 系统状态
4. **告警面板**
- 最近的告警
- 告警趋势
- 告警分布
---
**文档版本**: 1.0
**最后更新**: 2026-05-14
-448
View File
@@ -1,448 +0,0 @@
# 接口级权限系统 - 性能优化报告
**优化日期**: 2026-05-14
**优化范围**: 接口级权限系统(第9阶段)
**优化结果**: ✅ **成功** - 性能提升 50%+,缓存命中率 > 80%
---
## 执行摘要
本次性能优化针对权限系统的N+1查询问题进行了全面改进,通过实现权限矩阵缓存和成员身份缓存,显著提升了权限检查的性能。
**关键成果**
- ✅ 实现权限缓存机制,减少数据库查询
- ✅ 权限检查性能提升 **50%+**
- ✅ 缓存命中率 **> 80%**
- ✅ 列表操作性能提升 **30%+**
- ✅ 自动缓存失效机制,确保权限一致性
---
## 1. 性能瓶颈分析
### 1.1 N+1查询问题
**问题描述**
- `get_project_role_permissions()` 函数每次调用都查询整个权限矩阵
- 在列表操作中,每个项目都会触发权限检查
- 高并发场景下产生大量重复查询
**影响范围**
- 权限检查函数:`role_has_project_permission()`, `role_has_api_permission()`
- 列表操作:查询项目列表时需要逐个检查权限
- 并发场景:多个用户同时访问时,数据库查询激增
**性能指标**
- 单次权限检查:~5-10ms(包含数据库查询)
- 100次权限检查:~500-1000ms
- 列表操作(50个项目):~250-500ms
### 1.2 缺失的缓存机制
**问题描述**
- 无权限矩阵缓存
- 无成员身份缓存
- 无API端点权限缓存
- 每次请求都重新查询数据库
**影响范围**
- 重复查询相同的权限数据
- 数据库负载增加
- 响应时间变长
---
## 2. 优化方案
### 2.1 缓存架构
```
权限检查请求
检查缓存(内存)
├─ 缓存命中 → 返回缓存数据(<1ms)
└─ 缓存未命中 → 查询数据库 → 存储到缓存 → 返回数据
```
### 2.2 缓存层设计
**文件**: `/backend/app/core/permission_cache.py`
**核心功能**
1. **权限矩阵缓存**
- 缓存项目的完整权限矩阵
- TTL: 5分钟(可配置)
- 缓存键:`project_permissions:{study_id}`
2. **成员身份缓存**
- 缓存成员在项目中的角色
- TTL: 5分钟(可配置)
- 缓存键:`member_role:{study_id}:{user_id}`
3. **缓存失效机制**
- 权限更新时自动失效相关缓存
- 成员角色变更时自动失效缓存
- 支持单项失效和批量失效
### 2.3 缓存集成
**修改文件**: `/backend/app/core/project_permissions.py`
**修改内容**
1.`replace_project_role_permissions()` 后失效缓存
2.`replace_api_endpoint_permissions()` 后失效缓存
3. 导入缓存模块,集成缓存到权限检查
---
## 3. 性能测试结果
### 3.1 基准测试
**测试场景**: 权限检查性能(无缓存)
```
执行次数: 100
总耗时: ~500-1000ms
平均耗时: ~5-10ms/次
```
### 3.2 缓存性能测试
**测试场景**: 权限检查性能(有缓存)
```
第一次调用: ~5-10ms(数据库查询)
后续调用: <1ms(缓存命中)
性能提升: 5-10倍
```
### 3.3 缓存命中率
**测试场景**: 实际使用场景模拟
```
缓存命中率: > 80%
缓存失效率: < 20%
缓存有效期: 5分钟
```
### 3.4 列表操作性能
**测试场景**: 查询50个项目的权限
```
无缓存: ~250-500ms
有缓存: ~50-100ms
性能提升: 50%+
```
### 3.5 并发性能
**测试场景**: 100个并发权限检查
```
无缓存: ~1000-2000ms
有缓存: ~100-200ms
性能提升: 10倍+
```
---
## 4. 缓存策略
### 4.1 TTL策略
**权限矩阵缓存**: 5分钟
- 权限变更不频繁
- 5分钟内的数据一致性可接受
- 可根据需要调整
**成员身份缓存**: 5分钟
- 成员角色变更不频繁
- 5分钟内的数据一致性可接受
- 可根据需要调整
### 4.2 失效策略
**主动失效**
- 权限更新时立即失效
- 成员角色变更时立即失效
- 项目权限矩阵更新时失效所有成员缓存
**被动失效**
- 缓存过期时自动失效(TTL
- 系统重启时清除所有缓存
### 4.3 缓存监控
**缓存统计**
- 缓存项目数
- 缓存命中率
- 缓存失效频率
**监控指标**
```python
stats = cache.get_cache_stats()
# {
# 'project_permissions_count': 10,
# 'member_role_count': 50,
# 'total_count': 60,
# }
```
---
## 5. 性能改进总结
### 5.1 性能指标对比
| 指标 | 优化前 | 优化后 | 改进 |
|------|--------|--------|------|
| 单次权限检查 | 5-10ms | <1ms | 5-10倍 |
| 100次权限检查 | 500-1000ms | 50-100ms | 5-10倍 |
| 列表操作(50项) | 250-500ms | 50-100ms | 50%+ |
| 并发检查(100个) | 1000-2000ms | 100-200ms | 10倍+ |
| 缓存命中率 | 0% | >80% | - |
### 5.2 数据库查询减少
**优化前**
- 每次权限检查都查询数据库
- 列表操作:N个项目 = N次数据库查询
**优化后**
- 缓存命中时无数据库查询
- 缓存失效时才查询数据库
- 数据库查询减少 **80%+**
### 5.3 响应时间改进
**优化前**
- 权限检查:5-10ms
- 列表操作:250-500ms
**优化后**
- 权限检查:<1ms(缓存命中)
- 列表操作:50-100ms
- 改进:**50%+**
---
## 6. 实现细节
### 6.1 缓存初始化
```python
from app.core.permission_cache import get_permission_cache
cache = get_permission_cache()
```
### 6.2 权限检查集成
```python
# 自动使用缓存
allowed = await role_has_project_permission(
db, study_id, role, module, action
)
```
### 6.3 缓存失效
```python
# 权限更新时自动失效
await replace_project_role_permissions(db, study_id, permissions)
# 缓存已自动失效
```
### 6.4 缓存监控
```python
# 获取缓存统计
stats = cache.get_cache_stats()
print(f"缓存项目数: {stats['total_count']}")
```
---
## 7. 测试覆盖
### 7.1 性能测试
**文件**: `/backend/tests/test_permission_performance.py`
**测试用例**
- ✅ 基准测试(无缓存)
- ✅ 缓存性能测试
- ✅ 缓存命中率测试
- ✅ 并发权限检查测试
- ✅ 列表操作性能测试
- ✅ API权限检查性能测试
- ✅ 权限矩阵缓存测试
- ✅ 成员角色缓存测试
**测试数量**: 12个
### 7.2 安全测试
**文件**: `/backend/tests/test_permission_security.py`
**测试用例**
- ✅ 权限检查遗漏检测
- ✅ 权限配置完整性
- ✅ 缓存失效场景
- ✅ 权限更新立即生效
- ✅ 并发权限检查一致性
- ✅ ADMIN角色权限
- ✅ None角色拒绝
- ✅ 未知端点拒绝
**测试数量**: 20个
### 7.3 缓存测试
**文件**: `/backend/tests/test_permission_cache.py`
**测试用例**
- ✅ 缓存命中
- ✅ 缓存未命中
- ✅ 缓存过期
- ✅ 缓存失效
- ✅ 并发缓存访问
- ✅ 缓存键生成
- ✅ 缓存统计
- ✅ 清除所有缓存
- ✅ 不同TTL缓存
- ✅ 全局缓存实例
- ✅ 缓存性能改进
**测试数量**: 15个
**总测试数**: 47个新增测试
---
## 8. 建议和最佳实践
### 8.1 缓存配置建议
**开发环境**
```python
cache = PermissionCache(default_ttl=60) # 1分钟
```
**生产环境**
```python
cache = PermissionCache(default_ttl=300) # 5分钟
```
### 8.2 监控建议
**定期检查缓存统计**
```python
stats = cache.get_cache_stats()
if stats['total_count'] > 1000:
# 缓存项目过多,考虑清除
cache.clear_all()
```
### 8.3 故障排除
**缓存不生效**
1. 检查缓存是否被正确初始化
2. 检查缓存是否被意外失效
3. 检查缓存TTL是否过短
**缓存数据不一致**
1. 检查缓存失效机制是否正确
2. 检查是否有绕过缓存的直接数据库查询
3. 检查是否有并发修改问题
---
## 9. 后续优化方向
### 9.1 短期优化(第10阶段)
1. **实现分布式缓存**
- 使用 Redis 替代内存缓存
- 支持多进程/多服务器场景
2. **添加缓存预热**
- 系统启动时预加载常用权限
- 减少冷启动时的缓存未命中
3. **实现缓存统计和监控**
- 记录缓存命中率
- 监控缓存大小
- 告警缓存异常
### 9.2 中期优化(第11阶段)
1. **实现权限预加载**
- 用户登录时预加载权限
- 减少权限检查时的缓存未命中
2. **实现权限变更通知**
- 权限变更时通知相关用户
- 实时更新客户端权限信息
3. **实现权限审计日志**
- 记录权限变更历史
- 支持权限变更追溯
### 9.3 长期优化
1. **实现权限预测**
- 基于用户行为预测权限需求
- 提前加载可能需要的权限
2. **实现权限优化**
- 分析权限使用模式
- 优化权限配置
---
## 10. 性能优化总结
### 10.1 关键成果
**性能提升 50%+**
- 权限检查:5-10倍
- 列表操作:50%+
- 并发场景:10倍+
**缓存命中率 > 80%**
- 大多数权限检查都命中缓存
- 数据库查询减少 80%+
**自动缓存失效**
- 权限更新时自动失效
- 确保数据一致性
**完整的测试覆盖**
- 47个新增测试
- 性能、安全、缓存全覆盖
### 10.2 实施建议
1. **立即应用**
- 部署缓存机制到生产环境
- 监控缓存性能
2. **短期改进**
- 实现分布式缓存(Redis
- 添加缓存监控告警
3. **长期规划**
- 实现权限预加载
- 实现权限变更通知
- 实现权限审计日志
---
**优化完成日期**: 2026-05-14
**优化员**: Claude Haiku 4.5
**优化状态**: ✅ 完成
-198
View File
@@ -1,198 +0,0 @@
# 权限系统迁移 - 测试验证报告
## 执行摘要
权限系统从模块级权限完全迁移到接口级权限,并实现了前置权限检查机制。所有测试均已通过,系统稳定性得到验证。
## 测试覆盖范围
### 1. 单元测试 - 接口级权限检查 (12 个测试)
**文件**: `tests/test_api_permissions.py`
| 测试用例 | 目的 | 状态 |
|---------|------|------|
| test_api_permission_check_allowed | 验证接口级权限允许 | ✓ PASS |
| test_api_permission_check_denied | 验证接口级权限拒绝 | ✓ PASS |
| test_api_permission_fallback_to_module_level | 验证回退到模块级权限 | ✓ PASS |
| test_api_permission_fallback_denied | 验证模块级权限拒绝 | ✓ PASS |
| test_admin_always_allowed | 验证ADMIN角色总是被允许 | ✓ PASS |
| test_api_permission_priority_over_module | 验证接口级权限优先于模块级 | ✓ PASS |
| test_api_permission_read_endpoint | 验证读取端点权限 | ✓ PASS |
| test_api_permission_different_endpoints | 验证不同端点权限独立 | ✓ PASS |
| test_api_permission_different_roles | 验证不同角色权限独立 | ✓ PASS |
| test_api_permission_different_studies | 验证不同项目权限独立 | ✓ PASS |
| test_api_permission_none_role | 验证None角色权限检查 | ✓ PASS |
| test_api_permission_unknown_endpoint | 验证未知端点权限检查 | ✓ PASS |
### 2. 单元测试 - 前置权限检查 (12 个测试)
**文件**: `tests/test_prerequisite_permissions.py`
| 测试用例 | 目的 | 状态 |
|---------|------|------|
| test_prerequisite_permission_satisfied | 验证前置权限满足 | ✓ PASS |
| test_prerequisite_permission_missing | 验证前置权限缺失 | ✓ PASS |
| test_prerequisite_permission_not_configured | 验证前置权限未配置 | ✓ PASS |
| test_multiple_prerequisites_all_satisfied | 验证多个前置权限都满足 | ✓ PASS |
| test_multiple_prerequisites_one_missing | 验证多个前置权限中有一个缺失 | ✓ PASS |
| test_get_missing_prerequisites_empty | 验证获取缺失权限 - 无缺失 | ✓ PASS |
| test_get_missing_prerequisites_single | 验证获取缺失权限 - 单个缺失 | ✓ PASS |
| test_get_missing_prerequisites_multiple | 验证获取缺失权限 - 多个缺失 | ✓ PASS |
| test_prerequisite_check_disabled | 验证禁用前置权限检查 | ✓ PASS |
| test_admin_bypasses_prerequisites | 验证ADMIN角色绕过前置权限 | ✓ PASS |
| test_prerequisite_with_no_prerequisites_operation | 验证无前置权限的操作 | ✓ PASS |
| test_prerequisite_missing_not_configured | 验证前置权限未配置时的缺失 | ✓ PASS |
### 3. 集成测试 - 迁移的API端点 (22 个测试)
**文件**: `tests/test_migrated_endpoints.py`
覆盖的端点:
- **Subjects**: create, list, read, update, delete (5 个)
- **Visits**: create, list, read, update, delete (5 个)
- **Risk Issues**: create, list, read, update, delete (5 个)
- **Fee Contracts**: create, list, read, update, delete (5 个)
- **Finance Contracts**: create, list, read, update, delete (5 个)
- **Fee Payments**: create, update, delete (3 个)
所有端点测试均通过,验证了接口级权限的正确实现。
## 测试结果统计
```
总测试数: 46
通过: 46 ✓
失败: 0
覆盖率: 100%
```
## 关键验证点
### 1. 权限检查优先级
✓ 接口级权限优先于模块级权限
✓ 接口级权限未配置时正确回退到模块级权限
✓ ADMIN角色总是被允许
### 2. 前置权限机制
✓ 单个前置权限检查正确
✓ 多个前置权限检查正确
✓ 缺失前置权限被正确识别
✓ 前置权限可被禁用(用于递归检查)
✓ ADMIN角色绕过前置权限检查
### 3. 权限隔离
✓ 不同端点的权限独立
✓ 不同角色的权限独立
✓ 不同项目的权限独立
✓ 权限配置不会相互影响
### 4. 边界情况
✓ None角色被正确拒绝
✓ 未知端点被正确拒绝
✓ 无前置权限的操作正常工作
## 前置权限配置验证
系统中已配置的前置权限依赖关系:
```
subjects:create → sites:read
subjects:update → sites:read
subjects:delete → sites:read
visits:create → subjects:read, sites:read
visits:update → subjects:read, sites:read
visits:delete → subjects:read, sites:read
risk_issues:create → subjects:read, sites:read
risk_issues:update → subjects:read, sites:read
risk_issues:delete → subjects:read, sites:read
finance_contracts:* → sites:read
fees_contracts:* → sites:read
drug_shipments:* → sites:read
subject_pds:create → subjects:read, sites:read
subject_pds:update → subjects:read, sites:read
monitoring_audit:* → sites:read
```
所有前置权限配置均已验证正确。
## API端点迁移验证
### 第1批 (23 个端点)
- subjects: 5 个端点 ✓
- visits: 5 个端点 ✓
- aes (risk_issues): 5 个端点 ✓
- monitoring_visit_issues: 7 个端点 ✓
### 第2批 (11 个端点)
- members: 5 个端点 ✓
- sites: 4 个端点 ✓
- project_milestones: 2 个端点 ✓
### 第3批 (15 个端点)
- finance_contracts: 5 个端点 ✓
- fees_contracts: 5 个端点 ✓
- drug_shipments: 5 个端点 ✓
### 第4批 (19 个端点)
- startup endpoints: 19 个端点 ✓
**总计**: 68 个API端点已成功迁移到接口级权限
## 权限管理API验证
新增的权限管理端点:
| 端点 | 功能 | 状态 |
|------|------|------|
| GET /api-permissions/operations | 获取所有权限操作及前置权限 | ✓ 实现 |
| GET /api-permissions/operations/prerequisites | 获取所有操作的前置权限依赖 | ✓ 实现 |
| GET /api-permissions/{endpoint_key}/prerequisites | 检查特定操作的缺失前置权限 | ✓ 实现 |
| GET /api-permissions | 获取项目权限矩阵 | ✓ 实现 |
| PUT /api-permissions | 更新项目权限矩阵 | ✓ 实现 |
## 性能验证
- 权限检查响应时间: < 10ms (单个权限)
- 前置权限检查响应时间: < 50ms (多个前置权限)
- 数据库查询优化: 使用索引,避免N+1查询
## 向后兼容性
✓ 模块级权限表保留,用于历史数据
✓ 接口级权限未配置时自动回退到模块级权限
✓ 现有的权限配置继续有效
✓ 迁移过程中无需修改数据库数据
## 安全性验证
✓ ADMIN角色权限检查正确
✓ 权限隔离完整
✓ 前置权限检查防止权限泄露
✓ 缺失权限被正确识别和报告
## 建议
1. **监控**: 在生产环境中监控权限检查的性能
2. **审计**: 记录所有权限变更操作
3. **文档**: 更新用户文档,说明新的权限系统
4. **培训**: 对管理员进行权限管理培训
## 结论
权限系统迁移已完成,所有测试均通过。系统已准备好用于生产环境。
- ✓ 接口级权限系统完全实现
- ✓ 前置权限检查机制正常工作
- ✓ 68个API端点已迁移
- ✓ 向后兼容性保证
- ✓ 所有测试通过 (46/46)
-492
View File
@@ -1,492 +0,0 @@
# 接口级权限系统 - 完整实现总结
**项目完成日期**: 2026-05-14
**总工作量**: 40小时(预计40小时)
**项目状态**: ✅ **完成**
---
## 项目概述
本项目成功实现了从模块级权限到接口级权限的系统迁移,包括:
- 核心基础设施建设(第1-6阶段)
- 端点迁移(第7-8阶段,94个端点)
- 安全审计与性能优化(第9阶段)
- 监控与告警(第10阶段)
---
## 完成情况总结
### 📊 阶段完成统计
| 阶段 | 目标 | 状态 | 工作量 |
|------|------|------|--------|
| 1-6 | 核心基础设施 | ✅ 完成 | 12小时 |
| 7 | 迁移第2批(9个端点) | ✅ 完成 | 4小时 |
| 8 | 迁移第3批(63个端点) | ✅ 完成 | 12小时 |
| 9 | 安全审计与性能优化 | ✅ 完成 | 10小时 |
| 10 | 监控与告警 | ✅ 完成 | 6小时 |
| **总计** | | **✅ 完成** | **44小时** |
### 🎯 关键成果
#### 1️⃣ 端点迁移(94个)
- **第1批**: 22个端点(subjects, risk_issues, fees, finance_contracts
- **第2批**: 9个端点(members, sites
- **第3批**: 63个端点(12个模块)
- **总计**: 94个端点全部迁移完成
#### 2️⃣ 性能优化
- **性能提升**: 50%+(权限检查5-10倍)
- **缓存命中率**: > 80%
- **数据库查询**: 减少80%+
- **列表操作**: 性能提升50%+
#### 3️⃣ 安全性
- **权限检查覆盖率**: 100%(94个端点全部受保护)
- **权限配置完整性**: 优秀(101个端点完整配置)
- **ADMIN角色处理**: 一致且安全
- **安全审计**: 已完成
#### 4️⃣ 测试覆盖
- **总测试数**: 196个
- **新增测试**: 87个
- **测试通过率**: 100%
- **代码覆盖率**: 85%+
#### 5️⃣ 监控告警
- **监控API**: 6个端点
- **监控指标**: 20+个
- **告警类型**: 2+种
- **健康评分**: 0-100分
---
## 技术实现详解
### 核心架构
```
请求到达
FastAPI依赖注入 → require_api_permission()
权限检查(带缓存)
├─ 缓存命中 → 返回结果(<1ms)
└─ 缓存未命中 → 数据库查询 → 缓存存储
权限检查通过 → 执行业务逻辑
权限检查失败 → 返回403 Forbidden
监控记录 → 指标收集 → 告警生成
```
### 关键模块
| 模块 | 文件 | 功能 |
|------|------|------|
| 权限检查 | `project_permissions.py` | 接口级权限检查 |
| 权限缓存 | `permission_cache.py` | 权限矩阵缓存 |
| 权限监控 | `permission_monitor.py` | 性能指标收集 |
| 权限配置 | `api_permissions.py` | 权限配置注册表 |
| 权限管理API | `api_permissions.py` | 权限管理端点 |
| 监控API | `permission_monitoring.py` | 监控端点 |
### 数据库模型
| 模型 | 表名 | 用途 |
|------|------|------|
| `ApiEndpointPermission` | `api_endpoint_permissions` | 接口级权限存储 |
| `ApiEndpointRegistry` | `api_endpoint_registries` | 接口注册表 |
| `StudyRolePermission` | `study_role_permissions` | 模块级权限(向后兼容) |
---
## 性能指标
### 权限检查性能
| 场景 | 优化前 | 优化后 | 改进 |
|------|--------|--------|------|
| 单次检查 | 5-10ms | <1ms | **5-10倍** |
| 100次检查 | 500-1000ms | 50-100ms | **5-10倍** |
| 列表操作(50项) | 250-500ms | 50-100ms | **50%+** |
| 并发检查(100个) | 1000-2000ms | 100-200ms | **10倍+** |
### 缓存效率
| 指标 | 数值 |
|------|------|
| 缓存命中率 | > 80% |
| 缓存未命中率 | < 20% |
| 数据库查询减少 | 80%+ |
| 缓存项目数 | 60+ |
### 系统资源
| 资源 | 数值 |
|------|------|
| 代码行数 | 3000+行 |
| 测试代码 | 2000+行 |
| 文档 | 5000+字 |
| 新增API端点 | 6个 |
---
## 测试覆盖
### 测试统计
| 测试类型 | 数量 | 状态 |
|---------|------|------|
| 权限检查测试 | 12个 | ✅ 通过 |
| 权限管理API测试 | 11个 | ✅ 通过 |
| 权限配置测试 | 13个 | ✅ 通过 |
| 已迁移端点测试 | 73个 | ✅ 通过 |
| 性能测试 | 12个 | ✅ 通过 |
| 安全测试 | 20个 | ✅ 通过 |
| 缓存测试 | 15个 | ✅ 通过 |
| 监控测试 | 30个 | ✅ 通过 |
| 监控API测试 | 10个 | ✅ 通过 |
| **总计** | **196个** | **✅ 全部通过** |
### 代码覆盖率
```
权限系统核心模块: 85%+
- project_permissions.py: 85%
- api_permissions.py: 100%
- permission_cache.py: 90%
- permission_monitor.py: 88%
```
---
## 文档生成
### 生成的文档
| 文档 | 内容 |
|------|------|
| `SECURITY_AUDIT.md` | 安全审计报告 |
| `PERFORMANCE_OPTIMIZATION.md` | 性能优化报告 |
| `MONITORING_DASHBOARD.md` | 监控仪表板文档 |
| `IMPLEMENTATION_SUMMARY.md` | 实现总结 |
| `TESTING_SUMMARY.md` | 测试总结 |
### 文档特点
- ✅ 详细的实现说明
- ✅ 完整的API文档
- ✅ 性能指标对比
- ✅ 故障排除指南
- ✅ 最佳实践建议
---
## 关键特性
### 1. 接口级权限控制
**细粒度权限控制**
- 支持 METHOD:/path 格式的端点权限
- 支持 94 个已迁移端点的权限管理
- 支持权限矩阵的动态配置
**向后兼容**
- 保留模块级权限支持
- 接口级权限优先于模块级权限
- 平滑的迁移路径
### 2. 性能优化
**权限缓存**
- 权限矩阵缓存(TTL: 5分钟)
- 成员身份缓存(TTL: 5分钟)
- 自动缓存失效机制
**性能提升**
- 权限检查性能提升 5-10 倍
- 缓存命中率 > 80%
- 数据库查询减少 80%+
### 3. 安全审计
**完整的安全检查**
- 权限检查覆盖率 100%
- 权限配置完整性验证
- ADMIN 角色处理一致性检查
**安全报告**
- 详细的安全审计报告
- 风险评估和建议
- 改进措施说明
### 4. 监控告警
**实时监控**
- 权限检查性能监控
- 缓存效率监控
- 系统健康评分
**告警机制**
- 慢速权限检查告警
- 权限检查错误告警
- 灵活的告警过滤和管理
### 5. 完整的测试
**全面的测试覆盖**
- 196 个测试用例
- 85%+ 代码覆盖率
- 100% 测试通过率
**多层次测试**
- 单元测试
- 集成测试
- 性能测试
- 安全测试
---
## 使用指南
### 权限检查
```python
# 在 FastAPI 端点中使用权限检查
@router.post("/subjects")
async def create_subject(
study_id: uuid.UUID,
_=Depends(require_api_permission("POST:/subjects")),
):
# 业务逻辑
pass
```
### 权限管理
```bash
# 获取权限矩阵
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/studies/{study_id}/api-permissions
# 更新权限矩阵
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"PM": {"POST:/subjects": true}}' \
http://localhost:8000/api/v1/studies/{study_id}/api-permissions
```
### 监控查询
```bash
# 获取权限系统指标
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/metrics
# 获取系统健康状态
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/health
# 获取告警列表
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/permission-monitoring/alerts
```
---
## 最佳实践
### 1. 权限配置
- ✅ 使用 `@register_api_endpoint` 装饰器注册端点
- ✅ 为每个端点配置 `default_roles`
- ✅ 定期审查权限配置的完整性
### 2. 性能优化
- ✅ 监控缓存命中率,目标 > 80%
- ✅ 监控权限检查响应时间,目标 < 10ms
- ✅ 定期检查数据库查询性能
### 3. 安全管理
- ✅ 定期进行安全审计
- ✅ 监控权限检查错误率
- ✅ 及时处理告警信息
### 4. 监控告警
- ✅ 设置监控告警规则
- ✅ 定期检查系统健康状态
- ✅ 建立告警响应机制
---
## 后续改进方向
### 短期改进(第11阶段)
1. **分布式缓存**
- 使用 Redis 替代内存缓存
- 支持多进程/多服务器场景
2. **权限预加载**
- 用户登录时预加载权限
- 减少冷启动时的缓存未命中
3. **权限变更通知**
- 权限变更时通知相关用户
- 实时更新客户端权限信息
### 中期改进
1. **权限审计日志**
- 记录权限变更历史
- 支持权限变更追溯
2. **权限预测**
- 基于用户行为预测权限需求
- 提前加载可能需要的权限
3. **权限优化**
- 分析权限使用模式
- 优化权限配置
### 长期改进
1. **资源级权限**
- 支持更细粒度的资源级权限控制
- 例如:只能查看自己创建的项目
2. **权限继承**
- 实现权限继承机制
- 简化权限配置
3. **权限模板**
- 创建权限模板
- 快速配置常见权限组合
---
## 项目成果
### 代码质量
- ✅ 代码覆盖率 85%+
- ✅ 测试通过率 100%
- ✅ 代码规范遵循
- ✅ 文档完整详细
### 性能指标
- ✅ 权限检查性能提升 5-10 倍
- ✅ 缓存命中率 > 80%
- ✅ 数据库查询减少 80%+
- ✅ 系统响应时间 < 10ms
### 安全性
- ✅ 权限检查覆盖率 100%
- ✅ 权限配置完整性优秀
- ✅ ADMIN 角色处理一致
- ✅ 安全审计已完成
### 可维护性
- ✅ 详细的文档说明
- ✅ 完整的测试覆盖
- ✅ 清晰的代码结构
- ✅ 灵活的扩展机制
---
## 总结
接口级权限系统已成功实现,包括:
**核心功能**
- 接口级权限检查和管理
- 权限矩阵配置和查询
- 权限管理 API
**性能优化**
- 权限缓存机制
- 性能提升 50%+
- 缓存命中率 > 80%
**安全保障**
- 完整的安全审计
- 权限检查覆盖率 100%
- ADMIN 角色处理一致
**监控告警**
- 实时监控 API
- 系统健康评分
- 灵活的告警机制
**测试覆盖**
- 196 个测试用例
- 85%+ 代码覆盖率
- 100% 测试通过率
**文档完善**
- 详细的实现文档
- 完整的 API 文档
- 全面的使用指南
---
**项目状态**: ✅ **完成**
**完成日期**: 2026-05-14
**总工作量**: 44小时
**代码行数**: 3000+行
**测试数量**: 196个
**文档字数**: 10000+字
---
## 附录:文件清单
### 新增文件
**核心模块**:
- `app/core/api_permissions.py` - 权限配置
- `app/core/project_permissions.py` - 权限检查(扩展)
- `app/core/permission_cache.py` - 权限缓存
- `app/core/permission_monitor.py` - 权限监控
- `app/core/permission_monitoring_middleware.py` - 监控中间件
**API 端点**:
- `app/api/v1/api_permissions.py` - 权限管理 API
- `app/api/v1/permission_monitoring.py` - 监控 API
**数据库模型**:
- `app/models/api_endpoint_permission.py` - 接口权限模型
- `app/models/api_endpoint_registry.py` - 接口注册表模型
**测试**:
- `tests/test_api_permissions.py` - 权限检查测试
- `tests/test_api_permissions_endpoints.py` - 权限管理 API 测试
- `tests/test_api_permissions_config.py` - 权限配置测试
- `tests/test_migrated_endpoints.py` - 已迁移端点测试(第1批)
- `tests/test_migrated_endpoints_batch2.py` - 已迁移端点测试(第2批)
- `tests/test_migrated_endpoints_batch3.py` - 已迁移端点测试(第3批)
- `tests/test_permission_performance.py` - 性能测试
- `tests/test_permission_security.py` - 安全测试
- `tests/test_permission_cache.py` - 缓存测试
- `tests/test_permission_monitoring.py` - 监控测试
- `tests/test_permission_monitoring_api.py` - 监控 API 测试
**文档**:
- `SECURITY_AUDIT.md` - 安全审计报告
- `PERFORMANCE_OPTIMIZATION.md` - 性能优化报告
- `MONITORING_DASHBOARD.md` - 监控仪表板文档
- `IMPLEMENTATION_SUMMARY.md` - 实现总结
- `TESTING_SUMMARY.md` - 测试总结
---
**项目完成!** 🎉
-363
View File
@@ -1,363 +0,0 @@
# 接口级权限系统 - 安全审计报告
**审计日期**: 2026-05-14
**审计范围**: 接口级权限系统(第8阶段完成后)
**审计结论**: ✅ **安全** - 权限系统设计合理,未发现严重安全漏洞
---
## 执行摘要
本次安全审计对接口级权限系统进行了全面评估,包括:
- 权限检查覆盖范围
- 权限配置完整性
- ADMIN角色处理一致性
- 潜在安全风险
**关键发现**
- ✅ 权限检查覆盖率 **100%**94个已迁移端点全部受保护)
- ✅ 权限配置完整性 **优秀**101个端点完整配置)
- ✅ ADMIN角色处理 **一致**(所有权限检查函数处理一致)
- ⚠️ MODULE_TO_ENDPOINTS 映射 **不完整**(缺失19个端点,但不影响权限检查)
---
## 1. 权限检查覆盖范围审计
### 1.1 审计结果
| 指标 | 数值 | 状态 |
|------|------|------|
| 已迁移端点总数 | 94 | ✅ |
| 已覆盖权限检查的端点 | 94 | ✅ |
| 未覆盖权限检查的端点 | 0 | ✅ |
| **权限检查覆盖率** | **100%** | ✅ |
### 1.2 权限装饰器分布
系统采用了多层防御策略,使用了4种权限装饰器:
| 装饰器类型 | 端点数 | 用途 |
|-----------|--------|------|
| `require_api_permission()` | 25 | API级别权限控制 |
| `require_study_permission()` | 62 | 项目级别权限控制 |
| `require_study_member()` | 4 | 项目成员验证 |
| `require_roles()` | 7 | 系统级别角色控制 |
| **总计** | **98** | - |
### 1.3 按模块的权限检查覆盖
**第1批模块**22个端点)
- ✅ subjects5个端点,100%覆盖
- ✅ risk_issues3个端点,100%覆盖
- ✅ fees8个端点,100%覆盖
- ✅ finance_contracts5个端点,100%覆盖
**第2批模块**9个端点)
- ✅ members5个端点,100%覆盖
- ✅ sites4个端点,100%覆盖
**第3批模块**63个端点)
- ✅ startup19个端点,100%覆盖
- ✅ project_permissions2个端点,100%覆盖
- ✅ overview1个端点,100%覆盖
- ✅ monitoring_visit_issues7个端点,100%覆盖
- ✅ drug_shipments5个端点,100%覆盖
- ✅ material_equipments5个端点,100%覆盖
- ✅ subject_pds4个端点,100%覆盖
- ✅ audit_logs3个端点,100%覆盖
- ✅ visits5个端点,100%覆盖
- ✅ knowledge_notes5个端点,100%覆盖
- ✅ subject_histories5个端点,100%覆盖
- ✅ project_milestones2个端点,100%覆盖
### 1.4 关键发现
**✅ 所有94个已迁移的端点都已正确配置权限装饰器**
- 没有发现任何未受保护的端点
- 所有端点都使用了适当的权限检查机制
- 权限装饰器配置与端点功能相匹配
- 采用了多层防御策略(API权限 + 项目权限 + 角色权限)
---
## 2. 权限配置完整性审计
### 2.1 API_ENDPOINT_PERMISSIONS 配置质量
| 检查项 | 结果 | 说明 |
|--------|------|------|
| 总端点数 | 101 | 包含94个已迁移 + 7个额外端点 |
| 完整配置的端点 | 101 | 100% |
| 缺失字段的端点 | 0 | 0% |
| **配置质量** | **优秀** | ✅ |
**配置字段检查**
- ✅ 所有端点都有 `module` 字段
- ✅ 所有端点都有 `action` 字段(仅包含有效值:read 或 write)
- ✅ 所有端点都有 `description` 字段
- ✅ 所有端点都有非空的 `default_roles` 字段
- ✅ 所有角色都是有效的(PM, CRA, PV, MEDICAL_REVIEW, IMP, QA
### 2.2 MODULE_TO_ENDPOINTS 映射完整性
| 指标 | 数值 | 状态 |
|------|------|------|
| API_ENDPOINT_PERMISSIONS 中的端点 | 101 | - |
| MODULE_TO_ENDPOINTS 中的端点 | 82 | ⚠️ |
| 缺失的端点 | 19 | ⚠️ |
| 多余的端点 | 0 | ✅ |
**缺失的19个端点分布**
- project_members5个(POST、GET、GET/candidates、PATCH、DELETE
- subjects:14个(访视、PDS、历史记录相关)
**影响评估**
- ⚠️ MODULE_TO_ENDPOINTS 映射不完整
- ✅ 但不影响权限检查,因为系统优先使用 API_ENDPOINT_PERMISSIONS
- ✅ 这可能是向后兼容性的故意设计
### 2.3 数据一致性检查
| 检查项 | 结果 |
|--------|------|
| 所有 MODULE_TO_ENDPOINTS 端点都在 API_ENDPOINT_PERMISSIONS 中 | ✅ |
| 所有端点的 action 在两个数据结构中一致 | ✅ |
| 没有重复的模块定义 | ✅ |
| **数据一致性** | **完全一致** |
### 2.4 关键发现
**✅ API_ENDPOINT_PERMISSIONS 配置完整且一致**
- 所有101个端点都有完整的权限配置
- 所有必需字段都已填充
- 数据一致性良好,没有冲突
**⚠️ MODULE_TO_ENDPOINTS 映射不完整**
- 缺失19个端点的映射
- 但不影响权限检查,因为系统优先使用 API_ENDPOINT_PERMISSIONS
- 建议在后续维护中补充完整的映射
---
## 3. ADMIN角色处理一致性审计
### 3.1 ADMIN角色处理的一致性评估
| 检查项 | 状态 | 说明 |
|--------|------|------|
| ADMIN在 role_has_project_permission() 中的处理 | ✅ 一致 | 直接返回True |
| ADMIN在 role_has_api_permission() 中的处理 | ✅ 一致 | 直接返回True |
| ADMIN在 require_study_roles() 中的处理 | ✅ 一致 | 通过allow_system_admin参数绕过 |
| ADMIN在 require_study_permission() 中的处理 | ✅ 一致 | 通过allow_system_admin参数绕过 |
| ADMIN在 require_api_permission() 中的处理 | ✅ 一致 | 通过allow_system_admin参数绕过 |
| ADMIN在 require_study_member() 中的处理 | ✅ 一致 | 直接返回当前用户 |
| ADMIN在权限矩阵初始化中的处理 | ✅ 一致 | 始终返回完全权限 |
| ADMIN在权限持久化中的处理 | ✅ 一致 | 不存储到数据库 |
| ADMIN在权限查询中的处理 | ✅ 一致 | 不需要查询 |
| **总体一致性** | **✅ 一致** | - |
### 3.2 ADMIN角色的设计特点
**优点**
1. ✅ ADMIN权限不存储在数据库中,避免了权限配置错误
2. ✅ 非ADMIN用户不能修改ADMIN用户的权限
3. ✅ 所有权限检查都在依赖注入层进行,难以绕过
4. ✅ 权限检查的顺序正确:先检查ADMIN,再检查其他权限
5. ✅ ADMIN角色检查在权限检查的最早阶段进行,避免了不必要的数据库查询
**缺点**
- ⚠️ 没有明确的文档说明ADMIN角色的行为
- ⚠️ 没有审计日志记录ADMIN用户的权限相关操作
### 3.3 发现的问题
#### 问题1:API权限端点中的ADMIN角色处理不一致
**位置**: `/backend/app/api/v1/api_permissions.py` (第56-85行)
**问题描述**
- 虽然ADMIN用户可以通过 `require_study_roles(["PM"])``allow_system_admin=True` 默认参数访问权限管理端点
- 但返回结果中明确排除了ADMIN角色的权限信息
- 这在逻辑上是一致的(因为ADMIN权限不需要配置),但可能会让用户困惑
**风险等级**: 🟡 **低** - 这是设计特性,不是安全漏洞
#### 问题2allow_system_admin 参数未被充分利用
**位置**: `/backend/app/core/deps.py`
**问题描述**
- 所有权限检查函数都有 `allow_system_admin: bool = True` 参数
- 但没有任何端点显式设置 `allow_system_admin=False`
- 这意味着所有端点都允许ADMIN用户绕过权限检查
**风险等级**: 🟢 **无** - 这是预期的设计,ADMIN用户应该有完全访问权限
### 3.4 关键发现
**✅ ADMIN角色处理一致且安全**
- 所有权限检查函数都正确处理ADMIN角色
- ADMIN权限不存储在数据库中,避免了配置错误
- 非ADMIN用户不能修改ADMIN用户的权限
- 权限检查的顺序和逻辑都是正确的
---
## 4. 潜在安全风险评估
### 4.1 已识别的风险
| 风险 | 概率 | 影响 | 缓解措施 | 优先级 |
|------|------|------|---------|--------|
| 权限检查遗漏 | 低 | 高 | 100%覆盖率已验证 | ✅ 已解决 |
| 权限配置错误 | 低 | 中 | 配置验证已实现 | ✅ 已解决 |
| ADMIN角色滥用 | 低 | 高 | 缺少审计日志 | 🟡 需要改进 |
| 权限缓存不一致 | 中 | 中 | 缓存机制未实现 | 🟡 第9阶段实现 |
| N+1查询问题 | 中 | 中 | 缓存机制未实现 | 🟡 第9阶段实现 |
### 4.2 建议的改进措施
#### 建议1:添加ADMIN操作审计日志(优先级:高)
在ADMIN用户执行权限相关操作时添加审计日志:
- ADMIN用户访问权限管理端点
- ADMIN用户修改其他用户的项目权限
- ADMIN用户修改项目权限矩阵
**实现位置**
- `/backend/app/api/v1/api_permissions.py`
- `/backend/app/api/v1/members.py`
#### 建议2:实现权限缓存机制(优先级:高)
在第9阶段实现权限缓存,解决N+1查询问题:
- 权限矩阵缓存(TTL: 5分钟)
- 成员身份缓存(TTL: 5分钟)
- 缓存失效机制
**实现位置**
- `/backend/app/core/permission_cache.py`(新建)
#### 建议3:明确文档化ADMIN角色的行为(优先级:中)
在代码中添加详细的文档说明ADMIN角色的处理方式:
- ADMIN用户在所有权限检查中都被视为拥有完全权限
- ADMIN权限不存储在数据库中
- ADMIN用户可以修改任何其他用户的项目权限
**实现位置**
- `/backend/app/core/deps.py` - 模块级文档
- `/backend/app/core/project_permissions.py` - 模块级文档
#### 建议4:补充MODULE_TO_ENDPOINTS映射(优先级:低)
补充缺失的19个端点到MODULE_TO_ENDPOINTS映射中:
- project_members5个端点
- subjects14个端点
**实现位置**
- `/backend/app/core/api_permissions.py`
---
## 5. 安全性评估总结
### 5.1 总体评估
| 维度 | 评分 | 说明 |
|------|------|------|
| 权限检查覆盖 | ⭐⭐⭐⭐⭐ | 100%覆盖,无遗漏 |
| 权限配置完整性 | ⭐⭐⭐⭐⭐ | 101个端点完整配置 |
| ADMIN角色处理 | ⭐⭐⭐⭐⭐ | 一致且安全 |
| 权限隔离 | ⭐⭐⭐⭐⭐ | 多层防御策略 |
| 审计日志 | ⭐⭐⭐⭐☆ | 缺少ADMIN操作审计 |
| **总体安全性** | ⭐⭐⭐⭐⭐ | **优秀** |
### 5.2 安全结论
**✅ 权限系统设计合理,安全性良好**
**关键安全点**
1. ✅ 权限检查覆盖率100%,所有端点都受保护
2. ✅ 权限配置完整且一致,没有配置错误
3. ✅ ADMIN角色处理一致,没有权限绕过漏洞
4. ✅ 采用多层防御策略,提高了安全性
5. ✅ 权限检查在依赖注入层进行,难以绕过
**需要改进的地方**
1. ⚠️ 缺少ADMIN操作审计日志
2. ⚠️ 缺少权限缓存机制(性能问题)
3. ⚠️ MODULE_TO_ENDPOINTS映射不完整(向后兼容性)
---
## 6. 验证清单
### 6.1 权限检查覆盖验证
- ✅ 所有API端点都使用权限装饰器
- ✅ 所有端点都在 `API_ENDPOINT_PERMISSIONS` 中定义
- ✅ 所有端点都有 `default_roles` 配置
- ✅ 权限配置映射完整
### 6.2 安全性验证
- ✅ ADMIN角色处理一致
- ✅ 权限检查优先级正确
- ✅ 权限隔离有效
- ✅ 没有发现权限绕过漏洞
### 6.3 配置完整性验证
- ✅ API_ENDPOINT_PERMISSIONS 完整
- ⚠️ MODULE_TO_ENDPOINTS 不完整(但不影响权限检查)
- ✅ 所有必需字段都已填充
- ✅ 数据一致性良好
---
## 7. 后续行动
### 立即行动(第9阶段)
1. 实现权限缓存机制(性能优化)
2. 添加ADMIN操作审计日志(安全增强)
3. 补充MODULE_TO_ENDPOINTS映射(完整性)
### 中期行动(第10阶段)
1. 实现权限系统监控和告警
2. 添加权限变更通知机制
3. 实现权限审计报告功能
### 长期行动
1. 实现资源级权限控制
2. 实现权限继承机制
3. 实现权限模板功能
---
## 附录:审计工具和方法
### 使用的审计工具
- 代码静态分析:Grep、Glob
- 代码审查:手工代码阅读
- 配置验证:配置文件检查
### 审计方法
1. 扫描所有API端点文件,检查权限装饰器覆盖
2. 验证权限配置的完整性和一致性
3. 检查ADMIN角色在所有权限检查函数中的处理方式
4. 评估潜在的安全风险
### 审计范围
- `/backend/app/api/v1/` - 所有API端点文件
- `/backend/app/core/project_permissions.py` - 权限检查函数
- `/backend/app/core/deps.py` - 依赖注入函数
- `/backend/app/core/api_permissions.py` - 权限配置
---
**审计完成日期**: 2026-05-14
**审计员**: Claude Haiku 4.5
**审计状态**: ✅ 完成
-265
View File
@@ -1,265 +0,0 @@
# 接口级权限系统 - 测试总结
## 测试执行结果
### 测试覆盖范围
| 测试文件 | 测试数量 | 状态 | 覆盖内容 |
|---------|--------|------|---------|
| `test_api_permissions.py` | 12 | ✅ 全部通过 | 权限检查函数、优先级、回退机制 |
| `test_api_permissions_endpoints.py` | 11 | ✅ 全部通过 | 权限管理API、权限矩阵操作 |
| `test_api_permissions_config.py` | 13 | ✅ 全部通过 | 权限配置验证、端点注册 |
| `test_migrated_endpoints.py` | 22 | ✅ 全部通过 | 已迁移端点的权限验证(第1批) |
| `test_migrated_endpoints_batch2.py` | 17 | ✅ 全部通过 | 已迁移端点的权限验证(第2批) |
| `test_migrated_endpoints_batch3.py` | 34 | ✅ 全部通过 | 已迁移端点的权限验证(第3批) |
| **总计** | **109** | ✅ **全部通过** | - |
### 代码覆盖率
```
Name Stmts Miss Cover
-------------------------------------------------------------
app/core/api_permissions.py 3 0 100%
app/core/project_permissions.py 114 17 85%
-------------------------------------------------------------
TOTAL 117 17 85%
```
**覆盖率达到 85%,满足 80% 的目标要求。**
## 测试详情
### 1. 权限检查函数测试 (test_api_permissions.py)
**测试场景:**
- ✅ 接口级权限允许
- ✅ 接口级权限拒绝
- ✅ 回退到模块级权限(允许)
- ✅ 回退到模块级权限(拒绝)
- ✅ ADMIN 角色总是被允许
- ✅ 接口级权限优先于模块级权限
- ✅ 读取端点权限检查
- ✅ 不同端点的权限检查
- ✅ 不同角色的权限检查
- ✅ 不同项目的权限隔离
- ✅ None 角色处理
- ✅ 未知端点处理
**关键验证:**
- 权限检查优先级正确(接口级 > 模块级)
- 向后兼容性保证(模块级权限回退)
- 角色隔离和项目隔离正确
### 2. 权限管理API测试 (test_api_permissions_endpoints.py)
**测试场景:**
- ✅ 获取空权限矩阵(返回默认权限)
- ✅ 获取自定义权限矩阵
- ✅ 替换单个角色权限
- ✅ 替换多个角色权限
- ✅ 拒绝权限设置
- ✅ 覆盖现有权限
- ✅ 多端点权限设置
- ✅ 不同项目权限隔离
- ✅ 空负载处理
- ✅ 部分权限更新
- ✅ 权限矩阵结构验证
**关键验证:**
- 权限矩阵格式正确:`{role: {endpoint_key: {allowed: bool}}}`
- 默认权限正确应用
- 权限覆盖和更新正确
- 项目隔离正确
### 3. 已迁移端点测试 (test_migrated_endpoints.py)
**测试端点:**
**参与者管理 (Subjects)**
- ✅ POST /subjects - 创建参与者
- ✅ GET /subjects - 查询参与者列表
- ✅ GET /subjects/{id} - 查询参与者详情
- ✅ PATCH /subjects/{id} - 更新参与者
- ✅ DELETE /subjects/{id} - 删除参与者
**不良事件 (Risk Issues)**
- ✅ POST /risk-issues - 创建不良事件
- ✅ GET /risk-issues - 查询不良事件列表
- ✅ GET /risk-issues/{id} - 查询不良事件详情
**费用管理 (Fees)**
- ✅ POST /fees/contracts - 创建费用合同
- ✅ GET /fees/contracts - 查询费用合同列表
- ✅ GET /fees/contracts/{id} - 查询费用合同详情
- ✅ PATCH /fees/contracts/{id} - 更新费用合同
- ✅ DELETE /fees/contracts/{id} - 删除费用合同
- ✅ POST /fees/contracts/{id}/payments - 创建费用分期
- ✅ PATCH /fees/payments/{id} - 更新费用分期
- ✅ DELETE /fees/payments/{id} - 删除费用分期
**财务合同 (Finance Contracts)**
- ✅ POST /finance/contracts - 创建财务合同
- ✅ GET /finance/contracts - 查询财务合同列表
- ✅ GET /finance/contracts/{id} - 查询财务合同详情
- ✅ PATCH /finance/contracts/{id} - 更新财务合同
- ✅ DELETE /finance/contracts/{id} - 删除财务合同
**关键验证:**
- 所有端点权限检查正确
- 权限拒绝时返回 403
- 权限允许时正常执行
### 4. 第3批已迁移端点测试 (test_migrated_endpoints_batch3.py)
**测试端点:**
**启动管理 (Startup)**
- ✅ POST /studies/{study_id}/startup/ethics - 创建伦理审批
- ✅ GET /studies/{study_id}/startup/ethics - 查询伦理审批列表
- ✅ POST /studies/{study_id}/startup/feasibility - 创建可行性评估
- ✅ POST /studies/{study_id}/startup/budget - 创建预算
- ✅ POST /studies/{study_id}/startup/timeline - 创建时间表
**项目权限管理 (Project Permissions)**
- ✅ GET /studies/{study_id}/project-permissions - 查询项目权限
- ✅ PUT /studies/{study_id}/project-permissions - 更新项目权限
**项目概览 (Overview)**
- ✅ GET /studies/{study_id}/overview - 查询项目概览
**监查问题 (Monitoring Issues)**
- ✅ POST /studies/{study_id}/monitoring-issues - 创建监查问题
- ✅ GET /studies/{study_id}/monitoring-issues - 查询监查问题列表
**药物发货 (Drug Shipments)**
- ✅ POST /studies/{study_id}/drug-shipments - 创建药物发货
- ✅ GET /studies/{study_id}/drug-shipments - 查询药物发货列表
**物资管理 (Materials)**
- ✅ POST /studies/{study_id}/materials - 创建物资
- ✅ GET /studies/{study_id}/materials - 查询物资列表
**参与者PDS (Subject PDS)**
- ✅ POST /studies/{study_id}/subject-pds - 创建参与者PDS
- ✅ GET /studies/{study_id}/subject-pds - 查询参与者PDS列表
**审计日志 (Audit Logs)**
- ✅ GET /studies/{study_id}/audit-logs - 查询审计日志列表
- ✅ POST /studies/{study_id}/audit-logs/export - 导出审计日志
**访视管理 (Visits)**
- ✅ POST /studies/{study_id}/visits - 创建访视
- ✅ GET /studies/{study_id}/visits - 查询访视列表
**知识库笔记 (Knowledge Notes)**
- ✅ POST /studies/{study_id}/knowledge-notes - 创建知识库笔记
- ✅ GET /studies/{study_id}/knowledge-notes - 查询知识库笔记列表
**参与者历史 (Subject Histories)**
- ✅ GET /studies/{study_id}/subject-histories - 查询参与者历史列表
- ✅ POST /studies/{study_id}/subject-histories/export - 导出参与者历史
**项目里程碑 (Milestones)**
- ✅ GET /studies/{study_id}/milestones - 查询项目里程碑列表
- ✅ PATCH /studies/{study_id}/milestones/{id} - 更新项目里程碑
**权限拒绝场景**
- ✅ CRA 无法执行启动管理写操作
- ✅ CRA 无法执行项目权限管理操作
**向后兼容性验证**
- ✅ startup 模块的模块级权限回退仍然有效
- ✅ drug_shipments 模块的模块级权限回退仍然有效
- ✅ startup 模块的接口级权限优先于模块级权限
- ✅ materials 模块的接口级权限优先于模块级权限
**权限矩阵一致性**
- ✅ 第3批模块的权限矩阵一致性验证
- ✅ ADMIN 角色总是被允许
**关键验证:**
- 所有端点权限检查正确
- 权限拒绝时返回 403
- 向后兼容性保证(模块级权限回退)
- 接口级权限优先级正确
## 修复的问题
### 1. StudyRolePermission 模型参数错误
**问题:** 测试使用了不存在的 `action``allowed` 参数
**解决:** 更新测试使用正确的 `can_read``can_write` 参数
### 2. 权限矩阵返回格式不匹配
**问题:** `get_api_endpoint_permissions` 返回 `{role: {endpoint_key: bool}}`,但测试期望 `{role: {endpoint_key: {allowed: bool}}}`
**解决:** 更新函数返回正确的嵌套字典格式
### 3. 默认权限未返回
**问题:** `get_api_endpoint_permissions` 在没有自定义权限时返回空字典
**解决:** 更新函数初始化所有角色和端点的默认权限
## 测试基础设施
### 数据库配置
- **类型:** SQLite 内存数据库
- **UUID 处理:** 自定义 GUID TypeDecorator 支持 SQLite
- **隔离:** 每个测试使用唯一的 study_code
### 测试框架
- **框架:** pytest + pytest-asyncio
- **异步支持:** AsyncSession 和 async/await
- **Fixtures** event_loop, test_engine, db_session
## 下一步工作
### 已完成
- ✅ 第1阶段:数据库设计
- ✅ 第2阶段:权限配置系统
- ✅ 第3阶段:权限检查依赖注入
- ✅ 第4阶段:API端点迁移(第1批)
- ✅ 第5阶段:权限管理API
- ✅ 第6阶段:测试和文档
- ✅ 第7阶段:迁移第2批模块(members, sites
- ✅ 第8阶段:迁移第3批模块(12个模块,63个端点)
## 下一步工作
### 已完成
- ✅ 第1阶段:数据库设计
- ✅ 第2阶段:权限配置系统
- ✅ 第3阶段:权限检查依赖注入
- ✅ 第4阶段:API端点迁移(第1批)
- ✅ 第5阶段:权限管理API
- ✅ 第6阶段:测试和文档
- ✅ 第7阶段:迁移第2批模块(members, sites
- ✅ 第8阶段:迁移第3批模块(12个模块,63个端点)
- ✅ 第9阶段:安全审计与性能优化
### 待完成
- [ ] 第10阶段:监控与告警
- [ ] 第11阶段:文档完善
## 性能指标
- **测试执行时间:** 0.40 秒
- **平均单个测试时间:** 3.7 毫秒
- **代码覆盖率:** 85%
- **总测试数:** 109 个
## 结论
接口级权限系统的核心功能已完全实现并通过全面测试。第3批模块(12个模块,63个端点)已成功迁移。系统具有:
- ✅ 细粒度的接口级权限控制
- ✅ 向后兼容的模块级权限回退
- ✅ 清晰的权限优先级
- ✅ 完整的权限管理API
- ✅ 高代码覆盖率(85%
- ✅ 109 个测试用例全部通过
**已迁移模块:**
- 第1批:subjects, risk_issues, fees, finance_contracts22 个端点)
- 第2批:members, sites9 个端点)
- 第3批:audit_logs, drug_shipments, knowledge_notes, material_equipments, monitoring_visit_issues, overview, project_milestones, project_permissions, startup, subject_histories, subject_pds, visits63 个端点)
**总计:94 个端点已迁移**
系统已准备好进行第9阶段的安全审计和性能优化。
+82 -5
View File
@@ -15,7 +15,7 @@ from sqlalchemy import func, select, desc
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.deps import get_current_user, get_db_session
from app.core.permission_monitor import evaluate_permission_system_health, get_permission_monitor
from app.core.permission_monitor import get_permission_monitor
from app.models.permission_access_log import PermissionAccessLog
from app.models.permission_metric_snapshot import PermissionMetricSnapshot
from app.models.security_access_log import SecurityAccessLog
@@ -31,10 +31,45 @@ router = APIRouter(prefix="/permission-monitoring", tags=["permission-monitoring
@router.get("/metrics", status_code=status.HTTP_200_OK)
async def get_permission_metrics(
db: AsyncSession = Depends(get_db_session),
_=Depends(get_current_user),
hours: int = Query(24, ge=1, le=720),
) -> dict:
"""从 permission_access_logs 实时聚合权限检查指标"""
start_time = datetime.now(timezone.utc) - timedelta(hours=hours)
result = await db.execute(
select(
func.count().label("total_checks"),
func.count().filter(PermissionAccessLog.allowed.is_(True)).label("allowed_checks"),
func.count().filter(PermissionAccessLog.allowed.is_(False)).label("denied_checks"),
func.coalesce(func.sum(PermissionAccessLog.elapsed_ms), 0).label("total_time_ms"),
func.coalesce(func.min(PermissionAccessLog.elapsed_ms), 0).label("min_time_ms"),
func.coalesce(func.max(PermissionAccessLog.elapsed_ms), 0).label("max_time_ms"),
func.coalesce(func.avg(PermissionAccessLog.elapsed_ms), 0).label("avg_time_ms"),
).where(PermissionAccessLog.created_at >= start_time)
)
row = result.one()
total = row.total_checks or 0
monitor = get_permission_monitor()
return monitor.get_metrics()
cache_metrics = monitor.metrics.cache_metrics.to_dict()
return {
"window_hours": hours,
"check_metrics": {
"total_checks": total,
"allowed_checks": row.allowed_checks or 0,
"denied_checks": row.denied_checks or 0,
"total_time": round(float(row.total_time_ms) / 1000, 3),
"min_time": round(float(row.min_time_ms) / 1000, 3),
"max_time": round(float(row.max_time_ms) / 1000, 3),
"avg_time": round(float(row.avg_time_ms) / 1000, 3),
"allow_rate": round(row.allowed_checks / total * 100, 2) if total else 0,
"deny_rate": round(row.denied_checks / total * 100, 2) if total else 0,
"error_rate": 0,
"errors": 0,
},
"cache_metrics": cache_metrics,
"uptime_seconds": monitor.metrics.uptime_seconds,
}
@router.get("/cache-stats", status_code=status.HTTP_200_OK)
@@ -78,12 +113,54 @@ async def clear_alerts(
@router.get("/health", status_code=status.HTTP_200_OK)
async def permission_system_health(
db: AsyncSession = Depends(get_db_session),
_=Depends(get_current_user),
) -> dict:
"""从 DB 聚合最近 1 小时数据评估权限系统健康状态"""
start_time = datetime.now(timezone.utc) - timedelta(hours=1)
result = await db.execute(
select(
func.count().label("total"),
func.count().filter(PermissionAccessLog.allowed.is_(False)).label("denied"),
func.coalesce(func.avg(PermissionAccessLog.elapsed_ms), 0).label("avg_ms"),
).where(PermissionAccessLog.created_at >= start_time)
)
row = result.one()
total = row.total or 0
avg_ms = float(row.avg_ms)
deny_rate = round(row.denied / total * 100, 2) if total else 0
monitor = get_permission_monitor()
metrics = monitor.get_metrics()
cache_stats = monitor.get_cache_stats()
return evaluate_permission_system_health(metrics, cache_stats)
cache_metrics = monitor.metrics.cache_metrics
health_score = 100
issues = []
if avg_ms > 10:
health_score -= 10
issues.append("权限检查响应时间过长")
if deny_rate > 50:
health_score -= 5
issues.append("权限拒绝率过高")
if (
cache_metrics.total_accesses >= 10
and cache_metrics.hit_rate < 50
):
health_score -= 10
issues.append("缓存命中率过低")
return {
"status": "healthy" if health_score >= 80 else "degraded" if health_score >= 50 else "unhealthy",
"health_score": max(0, health_score),
"issues": issues,
"last_hour": {
"total_checks": total,
"denied_checks": row.denied or 0,
"deny_rate": deny_rate,
"avg_elapsed_ms": round(avg_ms, 2),
},
"cache_stats": monitor.get_cache_stats(),
}
# ═══════════════════════════════════════════
+7 -4
View File
@@ -162,10 +162,13 @@ def require_api_permission(endpoint_key: str, *, allow_system_admin: bool = True
raise
finally:
elapsed_ms = (time.perf_counter() - start_time) * 1000
monitor = get_permission_monitor()
monitor.record_permission_check(
allowed=allowed, elapsed_time=elapsed_ms / 1000, error=error
)
if error is not None or elapsed_ms > 50:
from app.core.permission_monitor import get_permission_monitor
monitor = get_permission_monitor()
if error is not None:
monitor.record_error_alert(error)
else:
monitor.record_slow_check_alert(elapsed_ms)
_enqueue_permission_log(
study_id, current_user.id, endpoint_key,
membership.role_in_study, allowed, elapsed_ms, request
+30 -220
View File
@@ -1,16 +1,13 @@
"""权限系统监控
实现权限系统的监控功能,包括:
- 权限检查统计
- 缓存性能监控
- 异常检测
- 性能指标收集
收集缓存性能指标和告警信息。
权限检查统计数据(总次数、允许/拒绝、耗时)已持久化在
permission_access_logs 表中,通过 /metrics 端点实时聚合查询。
"""
from __future__ import annotations
import time
import uuid
from dataclasses import dataclass, field
from typing import Any
@@ -20,88 +17,28 @@ from app.core.permission_cache import get_permission_cache
CACHE_HIT_RATE_HEALTH_MIN_ACCESSES = 10
@dataclass
class PermissionCheckMetrics:
"""权限检查指标"""
total_checks: int = 0 # 总检查次数
allowed_checks: int = 0 # 允许的检查次数
denied_checks: int = 0 # 拒绝的检查次数
total_time: float = 0.0 # 总耗时(秒)
min_time: float = float("inf") # 最小耗时(秒)
max_time: float = 0.0 # 最大耗时(秒)
errors: int = 0 # 错误次数
@property
def avg_time(self) -> float:
"""平均耗时(秒)"""
if self.total_checks == 0:
return 0.0
return self.total_time / self.total_checks
@property
def allow_rate(self) -> float:
"""允许率(百分比)"""
if self.total_checks == 0:
return 0.0
return (self.allowed_checks / self.total_checks) * 100
@property
def deny_rate(self) -> float:
"""拒绝率(百分比)"""
if self.total_checks == 0:
return 0.0
return (self.denied_checks / self.total_checks) * 100
@property
def error_rate(self) -> float:
"""错误率(百分比)"""
if self.total_checks == 0:
return 0.0
return (self.errors / self.total_checks) * 100
def to_dict(self) -> dict[str, Any]:
"""转换为字典"""
return {
"total_checks": self.total_checks,
"allowed_checks": self.allowed_checks,
"denied_checks": self.denied_checks,
"total_time": round(self.total_time, 3),
"min_time": round(self.min_time, 3) if self.min_time != float("inf") else 0,
"max_time": round(self.max_time, 3),
"avg_time": round(self.avg_time, 3),
"allow_rate": round(self.allow_rate, 2),
"deny_rate": round(self.deny_rate, 2),
"error_rate": round(self.error_rate, 2),
"errors": self.errors,
}
@dataclass
class CacheMetrics:
"""缓存指标"""
total_accesses: int = 0 # 总访问次数
cache_hits: int = 0 # 缓存命中次数
cache_misses: int = 0 # 缓存未命中次数
cache_invalidations: int = 0 # 缓存失效次数
total_accesses: int = 0
cache_hits: int = 0
cache_misses: int = 0
cache_invalidations: int = 0
@property
def hit_rate(self) -> float:
"""缓存命中率(百分比)"""
if self.total_accesses == 0:
return 0.0
return (self.cache_hits / self.total_accesses) * 100
@property
def miss_rate(self) -> float:
"""缓存未命中率(百分比)"""
if self.total_accesses == 0:
return 0.0
return (self.cache_misses / self.total_accesses) * 100
def to_dict(self) -> dict[str, Any]:
"""转换为字典"""
return {
"total_accesses": self.total_accesses,
"cache_hits": self.cache_hits,
@@ -114,101 +51,37 @@ class CacheMetrics:
@dataclass
class PermissionSystemMetrics:
"""权限系统指标"""
check_metrics: PermissionCheckMetrics = field(default_factory=PermissionCheckMetrics)
cache_metrics: CacheMetrics = field(default_factory=CacheMetrics)
last_reset_time: float = field(default_factory=time.time)
start_time: float = field(default_factory=time.time)
@property
def uptime_seconds(self) -> float:
return time.time() - self.start_time
def reset(self) -> None:
"""重置所有指标"""
self.check_metrics = PermissionCheckMetrics()
self.cache_metrics = CacheMetrics()
self.last_reset_time = time.time()
def to_dict(self) -> dict[str, Any]:
"""转换为字典"""
return {
"check_metrics": self.check_metrics.to_dict(),
"cache_metrics": self.cache_metrics.to_dict(),
"uptime_seconds": time.time() - self.last_reset_time,
}
self.start_time = time.time()
class PermissionMonitor:
"""权限系统监控器
收集权限系统的运行指标,用于监控和告警。
"""
"""权限系统监控器(仅缓存指标 + 告警)"""
def __init__(self):
"""初始化监控器"""
self.metrics = PermissionSystemMetrics()
self._alerts: list[dict[str, Any]] = []
def record_permission_check(
self,
allowed: bool,
elapsed_time: float,
error: Exception | None = None,
) -> None:
"""记录权限检查
Args:
allowed: 是否允许
elapsed_time: 耗时(秒)
error: 错误对象(如果有)
"""
metrics = self.metrics.check_metrics
metrics.total_checks += 1
if allowed:
metrics.allowed_checks += 1
else:
metrics.denied_checks += 1
metrics.total_time += elapsed_time
metrics.min_time = min(metrics.min_time, elapsed_time)
metrics.max_time = max(metrics.max_time, elapsed_time)
if error:
metrics.errors += 1
self._check_error_alert(error)
# 检查性能告警
self._check_performance_alert(elapsed_time)
def record_cache_hit(self) -> None:
"""记录缓存命中"""
metrics = self.metrics.cache_metrics
metrics.total_accesses += 1
metrics.cache_hits += 1
self.metrics.cache_metrics.total_accesses += 1
self.metrics.cache_metrics.cache_hits += 1
def record_cache_miss(self) -> None:
"""记录缓存未命中"""
metrics = self.metrics.cache_metrics
metrics.total_accesses += 1
metrics.cache_misses += 1
self.metrics.cache_metrics.total_accesses += 1
self.metrics.cache_metrics.cache_misses += 1
def record_cache_invalidation(self) -> None:
"""记录缓存失效"""
self.metrics.cache_metrics.cache_invalidations += 1
def _check_performance_alert(self, elapsed_time: float) -> None:
"""检查性能告警
如果权限检查耗时过长,发出告警。
"""
if elapsed_time > 0.05: # 50ms
self._add_alert(
level="warning",
type="slow_permission_check",
message=f"权限检查耗时过长: {elapsed_time*1000:.2f}ms",
data={"elapsed_time": elapsed_time},
)
def _check_error_alert(self, error: Exception) -> None:
"""检查错误告警"""
def record_error_alert(self, error: Exception) -> None:
self._add_alert(
level="error",
type="permission_check_error",
@@ -216,55 +89,33 @@ class PermissionMonitor:
data={"error": str(error)},
)
def _add_alert(
self,
level: str,
type: str,
message: str,
data: dict[str, Any] | None = None,
) -> None:
"""添加告警
def record_slow_check_alert(self, elapsed_ms: float) -> None:
if elapsed_ms > 50:
self._add_alert(
level="warning",
type="slow_permission_check",
message=f"权限检查耗时过长: {elapsed_ms:.2f}ms",
data={"elapsed_ms": elapsed_ms},
)
Args:
level: 告警级别 (info, warning, error)
type: 告警类型
message: 告警消息
data: 额外数据
"""
alert = {
def _add_alert(self, level: str, type: str, message: str, data: dict[str, Any] | None = None) -> None:
self._alerts.append({
"timestamp": time.time(),
"level": level,
"type": type,
"message": message,
"data": data or {},
}
self._alerts.append(alert)
# 只保留最近1000条告警
})
if len(self._alerts) > 1000:
self._alerts = self._alerts[-1000:]
def get_alerts(self, level: str | None = None, limit: int = 100) -> list[dict[str, Any]]:
"""获取告警列表
Args:
level: 告警级别过滤(可选)
limit: 返回的最大告警数
Returns:
告警列表
"""
alerts = self._alerts
if level:
alerts = [a for a in alerts if a["level"] == level]
return alerts[-limit:]
def get_metrics(self) -> dict[str, Any]:
"""获取指标"""
return self.metrics.to_dict()
def get_cache_stats(self) -> dict[str, Any]:
"""获取缓存统计"""
cache = get_permission_cache()
return {
"cache_items": cache.get_cache_stats(),
@@ -272,20 +123,16 @@ class PermissionMonitor:
}
def reset_metrics(self) -> None:
"""重置指标"""
self.metrics.reset()
def clear_alerts(self) -> None:
"""清除所有告警"""
self._alerts.clear()
# 全局监控器实例
_permission_monitor: PermissionMonitor | None = None
def get_permission_monitor() -> PermissionMonitor:
"""获取全局权限监控器实例"""
global _permission_monitor
if _permission_monitor is None:
_permission_monitor = PermissionMonitor()
@@ -293,42 +140,5 @@ def get_permission_monitor() -> PermissionMonitor:
def set_permission_monitor(monitor: PermissionMonitor) -> None:
"""设置全局权限监控器实例(用于测试)"""
global _permission_monitor
_permission_monitor = monitor
def evaluate_permission_system_health(metrics: dict[str, Any], cache_stats: dict[str, Any]) -> dict[str, Any]:
"""根据权限系统指标评估健康状态"""
check_metrics = metrics["check_metrics"]
cache_metrics = metrics["cache_metrics"]
health_score = 100
issues = []
if check_metrics["error_rate"] > 1:
health_score -= 20
issues.append("权限检查错误率过高")
if (
cache_metrics["total_accesses"] >= CACHE_HIT_RATE_HEALTH_MIN_ACCESSES
and cache_metrics["hit_rate"] < 50
):
health_score -= 10
issues.append("缓存命中率过低")
if check_metrics["avg_time"] > 0.01:
health_score -= 10
issues.append("权限检查响应时间过长")
if check_metrics["deny_rate"] > 50:
health_score -= 5
issues.append("权限拒绝率过高")
return {
"status": "healthy" if health_score >= 80 else "degraded" if health_score >= 50 else "unhealthy",
"health_score": max(0, health_score),
"issues": issues,
"metrics": metrics,
"cache_stats": cache_stats,
}
@@ -1,80 +0,0 @@
"""权限系统监控中间件
自动收集权限检查的性能指标和告警信息。
"""
from __future__ import annotations
import time
from typing import Callable
from app.core.permission_monitor import get_permission_monitor
class PermissionMonitoringMiddleware:
"""权限监控中间件
在权限检查前后记录指标。
"""
def __init__(self):
"""初始化中间件"""
self.monitor = get_permission_monitor()
def record_check(
self,
func: Callable,
) -> Callable:
"""装饰器:记录权限检查指标
Args:
func: 权限检查函数
Returns:
装饰后的函数
"""
async def wrapper(*args, **kwargs):
start_time = time.time()
try:
result = await func(*args, **kwargs)
elapsed_time = time.time() - start_time
self.monitor.record_permission_check(
allowed=result,
elapsed_time=elapsed_time,
)
return result
except Exception as e:
elapsed_time = time.time() - start_time
self.monitor.record_permission_check(
allowed=False,
elapsed_time=elapsed_time,
error=e,
)
raise
return wrapper
def record_cache_hit(self) -> None:
"""记录缓存命中"""
self.monitor.record_cache_hit()
def record_cache_miss(self) -> None:
"""记录缓存未命中"""
self.monitor.record_cache_miss()
def record_cache_invalidation(self) -> None:
"""记录缓存失效"""
self.monitor.record_cache_invalidation()
# 全局中间件实例
_middleware: PermissionMonitoringMiddleware | None = None
def get_monitoring_middleware() -> PermissionMonitoringMiddleware:
"""获取全局监控中间件实例"""
global _middleware
if _middleware is None:
_middleware = PermissionMonitoringMiddleware()
return _middleware