317 lines
8.5 KiB
Markdown
317 lines
8.5 KiB
Markdown
# 权限、租户能力与数据范围职责边界说明
|
|
|
|
> 适用范围:`leaudit-platform` 当前 `RBAC + 租户主数据 + 多租户业务挂载` 体系
|
|
> 更新日期:2026-05-21
|
|
> 文档定位:专门解决“角色权限配置”和“租户管理里的功能开关/承载能力”看起来重复的问题,明确三者各自负责什么、不负责什么。
|
|
|
|
---
|
|
|
|
## 1. 先看结论
|
|
|
|
当前系统里确实存在“语义重叠感”,但不应该把这三类配置看成同一层:
|
|
|
|
1. `角色权限`
|
|
- 控制“人能不能操作”
|
|
2. `租户功能开关`
|
|
- 控制“某租户是否开放某类业务能力”
|
|
3. `租户业务承载能力`
|
|
- 控制“某租户能不能作为某类业务的数据归属方/挂载方”
|
|
4. `数据范围`
|
|
- 控制“当前人最终能看到哪些租户、哪些数据”
|
|
|
|
如果只看页面字段,不看后端职责边界,就会误以为“租户页和角色权限页都在管入口模块、文档上传、知识库”。
|
|
这正是当前用户感知混乱的根因。
|
|
|
|
---
|
|
|
|
## 2. 三层控制分别管什么
|
|
|
|
## 2.1 角色权限:管人
|
|
|
|
角色权限回答的问题是:
|
|
|
|
- 这个用户能不能进入某页面
|
|
- 这个用户能不能新建、编辑、删除、发布、导出
|
|
- 这个用户能不能调用某个接口
|
|
|
|
典型例子:
|
|
|
|
- `entry_module:create`
|
|
- `entry_module:update`
|
|
- `documents:upload:create`
|
|
- `rag:dataset:manage`
|
|
- `rbac:tenants:update`
|
|
|
|
结论:
|
|
|
|
- 角色权限只决定“用户有没有操作资格”
|
|
- 不决定“数据应该挂在哪个租户下”
|
|
- 也不决定“某个租户是否承载某类业务”
|
|
|
|
---
|
|
|
|
## 2.2 租户功能开关:管租户是否开放业务能力
|
|
|
|
租户功能开关回答的问题是:
|
|
|
|
- 这个租户是否启用了入口模块能力
|
|
- 这个租户是否启用了文档上传能力
|
|
- 这个租户是否启用了知识库能力
|
|
|
|
当前主字段:
|
|
|
|
- `sys_tenant_feature_flags.feature_key`
|
|
- 前端对应 `feature_keys`
|
|
|
|
当前真实语义应理解为:
|
|
|
|
- “该租户是否开放该业务能力”
|
|
- 更偏向租户产品能力配置,不是用户授权配置
|
|
|
|
结论:
|
|
|
|
- 功能开关不授予用户权限
|
|
- 它只决定“租户是否开放某业务”
|
|
|
|
---
|
|
|
|
## 2.3 租户业务承载能力:管数据能不能落到该租户
|
|
|
|
租户承载能力回答的问题是:
|
|
|
|
- 新建入口模块时,这个租户能不能被选为适用租户
|
|
- 新建文档、模板、知识库时,这个租户能不能作为归属租户
|
|
|
|
当前主字段:
|
|
|
|
- `can_host_entry_module`
|
|
- `can_host_documents`
|
|
- `can_host_rag`
|
|
- `can_host_templates`
|
|
|
|
这类字段和 `feature_keys` 很像,但不是一回事:
|
|
|
|
- `feature_keys` 更偏“租户是否开放该业务”
|
|
- `can_host_*` 更偏“租户能否被选为该业务的数据挂载对象”
|
|
|
|
结论:
|
|
|
|
- 承载能力不等于操作权限
|
|
- 也不完全等于功能开关
|
|
- 它主要服务于“业务归属”和“数据落点”控制
|
|
|
|
---
|
|
|
|
## 2.4 数据范围:管最终可见边界
|
|
|
|
数据范围回答的问题是:
|
|
|
|
- 这个用户最终能看到哪些租户的数据
|
|
- 这个用户能看全部、部门、自身、公共、关系型资源中的哪些部分
|
|
|
|
它最终会把下面几件事合并起来:
|
|
|
|
1. 用户身份
|
|
2. 角色权限
|
|
3. 用户归属租户
|
|
4. 目标资源归属租户
|
|
5. 模块策略
|
|
|
|
结论:
|
|
|
|
- 数据范围是最终访问边界
|
|
- 它不是租户主数据页里的布尔开关
|
|
- 也不是角色权限页里的单个 permission 就能表达清楚的
|
|
|
|
## 2.5 三层控制的防护性区别
|
|
|
|
这三层不是“配置位置不同的同一件事”,而是三道不同的防护闸:
|
|
|
|
1. `租户功能开关`
|
|
- 属于业务准入防护
|
|
- 防的是“该租户根本不该开放这类业务,却仍被看见或被使用”
|
|
2. `角色权限`
|
|
- 属于操作授权防护
|
|
- 防的是“租户虽然开放了该业务,但不是任何人都能新增、编辑、删除、发布”
|
|
3. `数据范围`
|
|
- 属于数据边界防护
|
|
- 防的是“有权限的人在操作时串看、串改到不属于自己范围的数据”
|
|
|
|
可以直接这样理解:
|
|
|
|
- `租户功能开关 = 模块级总闸`
|
|
- `角色权限 = 人的操作闸`
|
|
- `数据范围 = 数据行级边界闸`
|
|
|
|
任意一层缺失,风险都不同:
|
|
|
|
1. 只有功能开关,没有角色权限
|
|
- 结果:租户开了功能后,可能任何角色都能乱用
|
|
2. 只有角色权限,没有功能开关
|
|
- 结果:本不该承载该业务的租户,仍可能被误开放入口或误创建数据
|
|
3. 只有前两层,没有数据范围
|
|
- 结果:拥有权限的用户仍可能看到其他租户数据,形成越权或串租户泄漏
|
|
|
|
所以最终判断某个操作是否允许,不能只看一层,而应按下面顺序串起来判断:
|
|
|
|
1. 目标租户是否开放该业务
|
|
2. 当前用户是否具备该业务动作权限
|
|
3. 当前用户的数据范围是否允许访问该目标租户或目标资源
|
|
|
|
---
|
|
|
|
## 3. 为什么当前会让人觉得重复
|
|
|
|
当前用户会觉得重复,原因不是理解错了,而是系统现状确实还没完全收口:
|
|
|
|
1. 前端租户页把:
|
|
- `启用租户`
|
|
- `公共租户`
|
|
- `功能开关`
|
|
- `承载能力`
|
|
混在同一个表单区域里
|
|
2. 业务名称上和角色权限页高度重合
|
|
3. 部分下游模块还没有把 `can_host_*` 做成强执行链路
|
|
4. `feature_keys` 已经有使用点,但并不是所有模块都用同一模式接入
|
|
|
|
因此现在最准确的判断不是“完全重复”,而是:
|
|
|
|
- 设计上应该分层
|
|
- 落地上还处于过渡态
|
|
- 页面表达还不够清楚
|
|
|
|
补一句更贴近当前现状的话:
|
|
|
|
- 当前系统已经有三层模型雏形
|
|
- 但还没有在所有模块做到“租户能力先拦、权限再拦、数据范围最后收口”的稳定统一执行顺序
|
|
|
|
---
|
|
|
|
## 4. 当前代码里的真实分工
|
|
|
|
## 4.1 已经落到真实用途的部分
|
|
|
|
当前已经能确认的真实用途:
|
|
|
|
1. `feature_keys`
|
|
- 已被入口模块租户选择器消费
|
|
- `home.entry_module` 已有真实用途
|
|
2. `leaudit_entry_module_tenants`
|
|
- 已作为入口模块适用租户关系表使用
|
|
3. 用户当前上下文
|
|
- 已开始统一输出 `tenant_code / tenant_name`
|
|
4. 首页入口模块
|
|
- 已按用户租户 + 模块租户关系过滤
|
|
|
|
## 4.2 仍未完全闭环的部分
|
|
|
|
以下部分仍需继续补强:
|
|
|
|
1. `can_host_documents`
|
|
2. `can_host_rag`
|
|
3. `can_host_templates`
|
|
4. `feature_keys` 的统一消费规范
|
|
|
|
结论:
|
|
|
|
- 现在租户页里的部分字段已经不是摆设
|
|
- 但也还没有做到所有字段都形成“写入即生效”的全链路闭环
|
|
|
|
---
|
|
|
|
## 5. 最终建议的收口规则
|
|
|
|
## 5.1 角色权限页只做“人”的授权
|
|
|
|
角色权限页只保留:
|
|
|
|
- 页面访问权限
|
|
- 菜单权限
|
|
- 按钮权限
|
|
- 接口动作权限
|
|
- 数据范围策略
|
|
|
|
不要在角色权限页里表达:
|
|
|
|
- 某租户是否开放业务
|
|
- 某租户是否能承载业务数据
|
|
|
|
## 5.2 租户管理页只做“租户侧能力配置”
|
|
|
|
租户管理页应拆成 3 组:
|
|
|
|
1. 基础状态
|
|
- `启用租户`
|
|
- `公共租户`
|
|
2. 功能开关
|
|
- 某租户是否开放某业务能力
|
|
3. 业务承载能力
|
|
- 某租户能否作为某业务的数据归属方
|
|
|
|
并且页面必须明确提示:
|
|
|
|
- 这里不授予用户操作权限
|
|
- 用户是否能操作,仍由角色权限决定
|
|
|
|
## 5.3 数据范围执行器单独收口
|
|
|
|
数据范围不要继续分散在各业务模块里各写一套:
|
|
|
|
- 应把用户权限
|
|
- 用户租户
|
|
- 资源租户
|
|
- 公共数据策略
|
|
- 关系型特例
|
|
|
|
统一并入执行器。
|
|
|
|
---
|
|
|
|
## 6. 推荐页面文案
|
|
|
|
### 功能开关
|
|
|
|
说明:
|
|
|
|
- 决定该租户是否开放某类业务能力
|
|
- 不直接授予用户页面或按钮操作权限
|
|
|
|
### 业务承载能力
|
|
|
|
说明:
|
|
|
|
- 决定该租户能否作为该类业务的数据归属租户
|
|
- 会影响新建/编辑时该租户是否可被选中
|
|
|
|
### 角色权限
|
|
|
|
说明:
|
|
|
|
- 控制当前用户能不能访问页面、调用接口、执行动作
|
|
- 与租户侧能力配置是两层控制
|
|
|
|
---
|
|
|
|
## 7. 后续工程动作建议
|
|
|
|
优先级建议:
|
|
|
|
1. 前端租户页分组与文案改清楚
|
|
2. 为 `can_host_documents / can_host_rag / can_host_templates` 补下游强校验
|
|
3. 梳理 `feature_keys` 的统一消费规范
|
|
4. 统一把“租户是否可选”沉到服务层,不让前端自己猜
|
|
5. 最后再把数据范围执行器接到更多业务域
|
|
|
|
---
|
|
|
|
## 8. 一句话原则
|
|
|
|
后续所有实现都应遵守下面这个原则:
|
|
|
|
- `角色权限` 决定“谁能操作”
|
|
- `租户功能开关` 决定“租户开不开这类业务”
|
|
- `租户承载能力` 决定“数据能不能挂到这个租户”
|
|
- `数据范围` 决定“最终能看到哪些数据”
|
|
|
|
只要这四层不再混写,后面的权限和多租户改造就不会继续失控。
|