企业级数据权限方案 · 决策与执行解耦 · 不依赖数据库的行级/列级权限体系
版本 v1.0 · 2026-08-24
定位:一份可落地到 cmx-* 生态的**数据权限(Data Authorization)**完整设计。核心主张——把"谁能看什么"(决策)与"在哪里过滤"(执行)彻底解耦,让权限策略写一次、DB 无关、多点执行、可单测。
目录
- 问题重述:数据权限真的必须依赖数据库吗?
- 执行点谱系:三档方案的取舍
- 核心范式:PDP/PEP 分离 + 部分求值(残差约束)
- 总体架构:一芯多壳 + 约束 AST 为轴
- 领域模型:Subject / Resource / Decision / Constraint
- 约束 AST:四种权限范型的公共表示
- 数据库表结构设计
- 请求链路时序
- 维度权限 ⊕ 关系权限(ReBAC)
- 列脱敏(Column Masking)
- 缓存策略
- 与 cmx-* 生态的集成
- 核心接口(Rust)
- 防绕过与安全纵深
- 落地路线图 D0–D6
- FAQ 与常见误区
一、问题重述
你的原始判断是:
传统 ERP 数据权限,最终无非返回一段 SQL 拼进
WHERE,过滤数据,依赖数据库。
这句话把两件本应分开的事情绑死在了一起:
| 关注点 | 本质问题 | 传统做法 | 是否必须在 DB? |
|---|---|---|---|
| 决策(Decision) | 谁、对什么资源、能做什么 | 埋在 SQL 片段/存储过程里 | 否 |
| 执行(Enforcement) | 过滤实际发生在哪一层 | 拼进 WHERE,DB 执行 |
视场景而定 |
一旦把"决策"从"执行"里剥出来,"不依赖数据库"的空间就打开了:决策可以完全在应用层(策略引擎)完成,执行则退化成一个可替换的后端——SQL 只是其中一种,不是唯一。
一句话结论:传统方案依赖的不是"数据库"这个东西,而是"过滤逻辑与数据同库、且能表达成 SQL"这个耦合。打破耦合的钥匙,是一个语义中立的约束中间表示(约束 AST)。
二、执行点谱系
"数据在哪里被过滤"决定了你有多依赖数据库。存在三档,构成一条连续谱系:
| 档位 | 决策位置 | 执行位置 | DB 依赖 | 可分页/聚合 | 规则复杂度上限 |
|---|---|---|---|---|---|
| ① DB 原生 RLS | DB(策略函数) | DB 引擎 | 极高 | ✓ | 中(受 SQL 表达力限制) |
| ② 查询改写下推 | 应用层 | DB(拼 WHERE) | 中(可移植) | ✓ | 中 |
| ③ 拉取后过滤 | 应用层 | 应用内存 | 无 | ✗ | 任意 |
三档没有绝对优劣,正确的系统会同时用到 ②③,并让 ① 作为高敏兜底。关键洞察在于:
- ①(RLS) 是唯一真正"依赖数据库"的方案——逻辑写在
CREATE POLICY/ PL/pgSQL 里,锁死 DB 品牌,难以单测,跨服务无力。它的不可替代优点是防绕过:任何直连 DB 的查询都逃不掉。 - ②(查询改写) 才是绝大多数 ERP(若依
@DataScope、Hibernate@Filter、MyBatis 拦截器)的真身。它的决策逻辑其实在应用层,DB 只是被动执行 SQL。它依赖的不是"数据库",而是"能下推"。 - ③(拉取后过滤) 彻底不碰 DB 逻辑,规则任意复杂,但无法正确分页/count/聚合,只适合单对象或小结果集(详情页、单据校验)。
②③ 的分野,正是经典的 PDP / PEP 分离:决策点(Policy Decision Point)和执行点(Policy Enforcement Point)本就不必是同一个地方。
三、核心范式
3.1 PDP / PEP 分离
借鉴 XACML 的经典分层,把系统拆成两个角色:
- PDP(决策点):输入主体上下文与资源描述,输出"允许/拒绝 + 约束条件"。与任何数据库无关,纯逻辑,可 wasm、可单测。
- PEP(执行点):拿到 PDP 的决策,负责在具体数据源上执行过滤。SQL 下推、内存谓词、ES 查询,都是 PEP 的不同实现。
3.2 部分求值(Partial Evaluation)—— 本方案的灵魂
朴素的 PDP 只能回答"是/否"(permit(user, action, one_row) → bool),这正是档位③的困境:必须逐行拉取再判定。
突破口:让 PDP 做部分求值——把策略里"已知的上下文"(当前用户、角色、组织维度)代入求值,把"尚未知晓的资源字段"(owner、status、dept_id)保留为符号,最终吐出一棵只剩资源约束的残差谓词树:
策略: 可见 ⟺ dept_id ∈ 我的部门集 ∧ (owner = 我 ∨ status = 'public')
└── 已知(代入)──┘ └────── 未知(保留)──────┘
部分求值后的残差:
dept_id IN [1001, 1002] AND (owner = 'u42' OR status = 'public')
这棵残差树就是约束 AST。它:
- 与 DB 无关——是一棵纯数据结构,可序列化成 JSON;
- 可被任意后端编译——SQL 编译器把它变成
WHERE,内存编译器把它变成Fn(&Row) -> bool,ES 编译器把它变成bool query; - 一次决策,多处执行——同一棵 AST 可下推到 PG、内存过滤 CSV、查询 ES。
这就是"不依赖数据库"的精确含义:你依赖的是策略引擎,而 DB 只是众多编译目标之一。学术与工业界的同类实现:OPA 的 Compile/Partial Eval API、Oso 的 Data Filtering、AWS Cedar 的 partial evaluation。
四、总体架构
沿用你在 cmx-flowengine / cmx-rulesengine 上验证过的"一芯多壳"哲学:一个语义中立的内核 cmx-dataauth-core,外面包裹可插拔的策略源与执行后端。


320

被折叠的 条评论
为什么被折叠?



