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