|
|
|
|
|
# VBase 权限模型设计文档
|
|
|
|
|
|
|
|
|
|
|
|
## 核心原则
|
|
|
|
|
|
|
|
|
|
|
|
1. **Policy(策略)**:定义"什么权限可以访问哪些接口"
|
|
|
|
|
|
2. **Role(角色)**:Policy 的集合,简化授权
|
|
|
|
|
|
3. **授权关系**:用户通过 Role 或直接与 Policy 关联,并携带数据范围
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 表结构关系
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
|
|
|
|
│ Policy │◄────┤ PolicyRoute │ │ Role │
|
|
|
|
|
|
│ (策略定义) │ │ (策略路由绑定) │ │ (角色定义) │
|
|
|
|
|
|
└────────┬────────┘ └──────────────────┘ └────────┬────────┘
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ Many-to-Many │ Many-to-Many
|
|
|
|
|
|
│ │
|
|
|
|
|
|
▼ ▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ UserPermission (用户授权) │
|
|
|
|
|
|
│ ├─ user_id: 用户ID │
|
|
|
|
|
|
│ ├─ policy_code: 策略代码 (或直接关联 Policy) │
|
|
|
|
|
|
│ ├─ role_id: 角色ID (可选,与 policy_code 二选一) │
|
|
|
|
|
|
│ ├─ resource: 资源类型 (project/org/...) │
|
|
|
|
|
|
│ ├─ resource_id: 具体资源ID (proj-123, * 表示所有) │
|
|
|
|
|
|
│ ├─ scope: 权限范围 (owner/admin/member) │
|
|
|
|
|
|
│ └─ expire_at: 过期时间 (可选) │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 详细表设计
|
|
|
|
|
|
|
|
|
|
|
|
### 1. Policy(策略表)
|
|
|
|
|
|
|
|
|
|
|
|
定义权限的本质:对某资源可以执行什么操作
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| id | string | UUID |
|
|
|
|
|
|
| code | string | **唯一标识**,如 `project:read`, `project:write`, `project:admin` |
|
|
|
|
|
|
| resource | string | 资源类型:`user`, `org`, `project`, `*`(所有) |
|
|
|
|
|
|
| action | string | 操作类型:`create`, `read`, `update`, `delete`, `list`, `*`, `admin` |
|
|
|
|
|
|
| effect | string | `allow` / `deny` |
|
|
|
|
|
|
| scope | string | 作用域:`platform`(平台级) / `org`(组织级) |
|
|
|
|
|
|
| description | string | 描述 |
|
|
|
|
|
|
|
|
|
|
|
|
**示例数据:**
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 项目相关权限
|
|
|
|
|
|
('p1', 'project:create', 'project', 'create', 'allow', 'org', '创建项目')
|
|
|
|
|
|
('p2', 'project:read', 'project', 'read', 'allow', 'org', '查看项目')
|
|
|
|
|
|
('p3', 'project:write', 'project', 'write', 'allow', 'org', '编辑项目(包含read)')
|
|
|
|
|
|
('p4', 'project:delete', 'project', 'delete', 'allow', 'org', '删除项目')
|
|
|
|
|
|
('p5', 'project:admin', 'project', 'admin', 'allow', 'org', '项目管理员(包含所有)')
|
|
|
|
|
|
|
|
|
|
|
|
-- 通配权限
|
|
|
|
|
|
('p9', 'project:*', 'project', '*', 'allow', 'org', '所有项目权限')
|
|
|
|
|
|
('p0', '*:*', '*', '*', 'allow', 'platform', '超级管理员')
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**层级关系:**
|
|
|
|
|
|
```
|
|
|
|
|
|
project:admin > project:write > project:read
|
|
|
|
|
|
│ │ └─ project:create, project:delete
|
|
|
|
|
|
└──────────────┴──────────────────────────────────────────────
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2. PolicyRoute(策略路由绑定表)
|
|
|
|
|
|
|
|
|
|
|
|
Policy 与具体接口的绑定关系。一个 Policy 可以绑定多个路由。
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| id | string | UUID |
|
|
|
|
|
|
| policy_id | string | 关联 Policy.id |
|
|
|
|
|
|
| domain | string | 应用域,如 `vbase`, `myapp` |
|
|
|
|
|
|
| prefix | string | 路由前缀,如 `/api/v1` |
|
|
|
|
|
|
| pattern | string | 路由模式,如 `/projects`, `/projects/{id}` |
|
|
|
|
|
|
| method | string | HTTP 方法:`GET`, `POST`, `*`, ... |
|
|
|
|
|
|
| resource_id_param | string | 从 URL 提取资源ID的参数名,如 `id` |
|
|
|
|
|
|
| description | string | 接口描述 |
|
|
|
|
|
|
|
|
|
|
|
|
**示例数据:**
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- project:read 权限可以访问以下接口
|
|
|
|
|
|
('r1', 'p2', 'myapp', '/api/v1', '/projects', 'GET', '', '项目列表')
|
|
|
|
|
|
('r2', 'p2', 'myapp', '/api/v1', '/projects/{id}', 'GET', 'id', '项目详情')
|
|
|
|
|
|
('r3', 'p2', 'myapp', '/api/v1', '/projects/{id}/logs', 'GET', 'id', '项目日志')
|
|
|
|
|
|
|
|
|
|
|
|
-- project:write 权限
|
|
|
|
|
|
('r4', 'p3', 'myapp', '/api/v1', '/projects/{id}', 'PATCH', 'id', '更新项目')
|
|
|
|
|
|
('r5', 'p3', 'myapp', '/api/v1', '/projects/{id}/env', 'PUT', 'id', '更新环境变量')
|
|
|
|
|
|
|
|
|
|
|
|
-- project:create 权限
|
|
|
|
|
|
('r6', 'p1', 'myapp', '/api/v1', '/projects', 'POST', '', '创建项目')
|
|
|
|
|
|
|
|
|
|
|
|
-- project:admin 权限(包含所有)
|
|
|
|
|
|
('r7', 'p5', 'myapp', '/api/v1', '/projects/{id}/members', 'GET', 'id', '成员管理')
|
|
|
|
|
|
('r8', 'p5', 'myapp', '/api/v1', '/projects/{id}/settings', '*', 'id', '设置管理')
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 3. Role(角色表)
|
|
|
|
|
|
|
|
|
|
|
|
Policy 的集合,用于批量授权。
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| id | string | UUID |
|
|
|
|
|
|
| org_id | string | 组织ID(系统角色为空) |
|
|
|
|
|
|
| code | string | 角色代码:`admin`, `developer`, `viewer`, `owner` |
|
|
|
|
|
|
| name | string | 角色名称 |
|
|
|
|
|
|
| policy_codes | string | 关联的 Policy code 列表,逗号分隔 |
|
|
|
|
|
|
| is_system | bool | 是否系统预设角色 |
|
|
|
|
|
|
|
|
|
|
|
|
**示例数据:**
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 系统预设角色
|
|
|
|
|
|
('r1', '', 'owner', '所有者', 'project:admin,org:admin,*:*', true)
|
|
|
|
|
|
('r2', '', 'admin', '管理员', 'project:write,org:read,user:read', true)
|
|
|
|
|
|
('r3', '', 'developer', '开发者', 'project:write,resource:read', true)
|
|
|
|
|
|
('r4', '', 'viewer', '只读用户', 'project:read', true)
|
|
|
|
|
|
|
|
|
|
|
|
-- 组织自定义角色
|
|
|
|
|
|
('r5', 'org-123', 'pm', '项目经理', 'project:admin', false)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 4. UserPermission(用户授权表)
|
|
|
|
|
|
|
|
|
|
|
|
核心授权表,记录"谁对什么资源有什么权限"。
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| id | string | UUID |
|
|
|
|
|
|
| user_id | string | 用户ID |
|
|
|
|
|
|
| org_id | string | 组织ID(可选) |
|
|
|
|
|
|
| **policy_code** | string | **策略代码**(如 `project:read`) |
|
|
|
|
|
|
| **resource** | string | **资源类型**(如 `project`) |
|
|
|
|
|
|
| **resource_id** | string | **具体资源ID**(如 `proj-123` 或 `*`) |
|
|
|
|
|
|
| granted_by | string | 授权来源:`role`(通过角色) / `direct`(直接授权) |
|
|
|
|
|
|
| source_id | string | 来源ID(Role ID 或留空) |
|
|
|
|
|
|
| expire_at | timestamp | 过期时间(可选,永久有效为空) |
|
|
|
|
|
|
| created_at | timestamp | 创建时间 |
|
|
|
|
|
|
|
|
|
|
|
|
**关键设计:**
|
|
|
|
|
|
- `resource_id = *` 表示对该类型所有资源的权限
|
|
|
|
|
|
- 数据级权限通过具体的 `resource_id` 实现
|
|
|
|
|
|
- 用户最终权限 = 直接授权 + 角色授权 的并集
|
|
|
|
|
|
|
|
|
|
|
|
**示例数据:**
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 场景:用户 A
|
|
|
|
|
|
-- 1. 是项目 1 的 admin(直接授权)
|
|
|
|
|
|
('up1', 'user-a', 'org-1', 'project:admin', 'project', 'proj-1', 'direct', '', null)
|
|
|
|
|
|
|
|
|
|
|
|
-- 2. 是项目 2 的只读成员(直接授权)
|
|
|
|
|
|
('up2', 'user-a', 'org-1', 'project:read', 'project', 'proj-2', 'direct', '', null)
|
|
|
|
|
|
|
|
|
|
|
|
-- 3. 通过 developer 角色获得对所有项目的 write 权限
|
|
|
|
|
|
('up3', 'user-a', 'org-1', 'project:write', 'project', '*', 'role', 'role-dev-id', null)
|
|
|
|
|
|
|
|
|
|
|
|
-- 4. 临时授权,3天后过期
|
|
|
|
|
|
('up4', 'user-a', 'org-1', 'project:admin', 'project', 'proj-3', 'direct', '', '2025-02-20 00:00:00')
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 权限检查流程
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
请求: GET /api/v1/projects/proj-123/settings
|
|
|
|
|
|
Header: X-Org-ID: org-1
|
|
|
|
|
|
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────┐
|
|
|
|
|
|
│ 1. 路由匹配 │
|
|
|
|
|
|
│ domain=myapp + method=GET + │
|
|
|
|
|
|
│ pattern=/projects/{id}/settings │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 找到: PolicyRoute.r8 │
|
|
|
|
|
|
│ 对应: Policy.p5 (project:admin) │
|
|
|
|
|
|
│ 提取: resource_id = "proj-123" │
|
|
|
|
|
|
└─────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────┐
|
|
|
|
|
|
│ 2. 获取用户权限列表 │
|
|
|
|
|
|
│ SELECT * FROM user_permissions │
|
|
|
|
|
|
│ WHERE user_id = 'user-a' │
|
|
|
|
|
|
│ AND resource = 'project' │
|
|
|
|
|
|
│ AND (resource_id = 'proj-123' │
|
|
|
|
|
|
│ OR resource_id = '*') │
|
|
|
|
|
|
└─────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────┐
|
|
|
|
|
|
│ 3. 权限层级匹配 │
|
|
|
|
|
|
│ 需要的权限: project:admin │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 用户有的权限: │
|
|
|
|
|
|
│ - project:admin (proj-1) ❌ │
|
|
|
|
|
|
│ - project:read (proj-2) ❌ │
|
|
|
|
|
|
│ - project:write (*) ❌ │
|
|
|
|
|
|
│ (write < admin) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 结果: 拒绝 ❌ │
|
|
|
|
|
|
└─────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
拒绝,返回 X-Required-URL: /apply-perm?resource=project&action=admin&id=proj-123
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 场景覆盖
|
|
|
|
|
|
|
|
|
|
|
|
### 场景1:用户是项目 owner,拥有所有权限
|
|
|
|
|
|
```sql
|
|
|
|
|
|
INSERT INTO user_permissions (user_id, policy_code, resource, resource_id)
|
|
|
|
|
|
VALUES ('user-a', 'project:admin', 'project', 'proj-1')
|
|
|
|
|
|
-- 自动包含 project:read/write/delete/create
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 场景2:平台管理员,拥有所有资源的所有权限
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 方案一:通配符
|
|
|
|
|
|
INSERT INTO user_permissions (user_id, policy_code, resource, resource_id)
|
|
|
|
|
|
VALUES ('admin', '*:*', '*', '*')
|
|
|
|
|
|
|
|
|
|
|
|
-- 方案二:绑定 owner 角色
|
|
|
|
|
|
-- Role.owner 包含 policy_codes: '*:*'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 场景3:组织管理员,拥有组织内所有项目权限
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 组织 admin 角色包含 project:*
|
|
|
|
|
|
-- 在组织上下文内生效
|
|
|
|
|
|
INSERT INTO user_permissions (user_id, org_id, policy_code, resource, resource_id, granted_by)
|
|
|
|
|
|
VALUES ('user-a', 'org-1', 'project:*', 'project', '*', 'role')
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 场景4:用户加入多个项目,权限不同
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 项目1: admin
|
|
|
|
|
|
INSERT INTO user_permissions VALUES ('u1', 'project:admin', 'project', 'proj-1', ...)
|
|
|
|
|
|
|
|
|
|
|
|
-- 项目2: read
|
|
|
|
|
|
INSERT INTO user_permissions VALUES ('u1', 'project:read', 'project', 'proj-2', ...)
|
|
|
|
|
|
|
|
|
|
|
|
-- 项目3: write
|
|
|
|
|
|
INSERT INTO user_permissions VALUES ('u1', 'project:write', 'project', 'proj-3', ...)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 与旧方案对比
|
|
|
|
|
|
|
|
|
|
|
|
| 维度 | 旧方案 (RoutePolicy) | 新方案 (Policy + UserPermission) |
|
|
|
|
|
|
|------|---------------------|--------------------------------|
|
|
|
|
|
|
| 核心思想 | 接口白名单 | 资源操作权限 |
|
|
|
|
|
|
| 配置粒度 | 每个接口单独配置 | 按 Resource + Action 配置 |
|
|
|
|
|
|
| 数据级权限 | 难实现 | 通过 resource_id 字段 |
|
|
|
|
|
|
| 通配授权 | 不支持 | 支持 `*` |
|
|
|
|
|
|
| 层级权限 | 无 | admin > write > read |
|
|
|
|
|
|
| 新增接口成本 | 高(需给所有角色加策略) | 低(绑定到 Policy 即可)|
|
|
|
|
|
|
| 角色数量 | 多(每项目配角色) | 少(复用系统角色 + 数据范围)|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
请确认此设计后,我开始修改代码。
|