mirror of https://github.com/veypi/OneAuth.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12 KiB
12 KiB
VBase 权限模型设计文档
核心原则
- Policy(策略):定义"什么权限可以访问哪些接口"
- Role(角色):Policy 的集合,简化授权
- 授权关系:用户通过 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 | 描述 |
示例数据:
-- 项目相关权限
('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 | 接口描述 |
示例数据:
-- 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 | 是否系统预设角色 |
示例数据:
-- 系统预设角色
('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实现 - 用户最终权限 = 直接授权 + 角色授权 的并集
示例数据:
-- 场景:用户 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,拥有所有权限
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:平台管理员,拥有所有资源的所有权限
-- 方案一:通配符
INSERT INTO user_permissions (user_id, policy_code, resource, resource_id)
VALUES ('admin', '*:*', '*', '*')
-- 方案二:绑定 owner 角色
-- Role.owner 包含 policy_codes: '*:*'
场景3:组织管理员,拥有组织内所有项目权限
-- 组织 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:用户加入多个项目,权限不同
-- 项目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 即可) |
| 角色数量 | 多(每项目配角色) | 少(复用系统角色 + 数据范围) |
请确认此设计后,我开始修改代码。