数据平台和普通业务系统最大的不同:用户可以在平台的计算引擎里跑代码——SQL、UDF、Notebook、JAR 作业。这决定了权限不能做成「前端藏按钮 + 接口加注解」,它是个架构问题:边界放在哪一层、异常时倒向哪一边、约束由谁作准。这篇以可私有化部署的数据平台「我的数据空间」(datastudiohappy.cn)为例,讲多租户权限体系的完整建法。
一、威胁模型:先想清楚防什么
三条主线,缺一条,设计就会漏一块:
1. 越权读写。 A 空间的用户不该碰到 B 空间的数据——且这条要在「用户能跑任意代码」的前提下依然成立。
2. 绕过平台、直连引擎。 鉴权全在控制面,而引擎端口在内网可达——原生 JDBC 直连过去,控制面那套判断一行都不会执行。
3. 降级路径静默放行。 解析失败、异常分支若默认「放过去」,那么解析不了的语句——往往正是最危险的那类——反而走了权限最松的路。
好的设计要把这三条变成结构上无从发生,而不是靠一段判断挡住。
二、原则一:数据面隔离——控制面鉴权不等于隔离
控制面鉴权是「判断谁能干什么」:带身份调 API,查 ACL,决定放不放行。它必要,但前提是所有访问都流经这道判断。而数据平台的现实是:作业和 Notebook 在引擎里执行,引擎拿存储凭证直接访问对象存储——凭证若对整个数据湖有权,代码一旦跑进引擎,控制面判过的 ACL 就全部作废。
所以机密性的硬边界必须沉到数据面:
「我的数据空间」的落地方式:
- 每空间一把存储小账号,只对本空间的存储前缀有读写权;
- 表(而非库)作为存储访问单元:逻辑库可以跨空间共享,但每张表的物理位置按建表者的空间前缀分配——小账号的权限范围与空间边界严丝合缝;
- 跨空间读 = 显式授权:把被授表的真实前缀加进读者空间小账号的访问策略,撤权即时失效,上层授权与底层存储权限永远联动一致;
- 平台维护作业(快照过期、文件整理等跨空间治理)保留大账号,但只跑平台固定代码、无用户代码入口——大账号加可信代码是安全的,大账号加用户代码才是洞。
跨空间授权在产品里是一个显式动作,授给哪个空间、什么权限,一目了然:
效果:即便应用层鉴权被完全绕过——直连引擎、跑任意代码——存储层 403 依然成立,最坏也只触到本空间本就授权的数据。引擎侧逐表 SQL 鉴权仍保留,但定位是纵深防御,守存储前缀表达不了的细粒度(owner 才能改表结构、列级隐私字段);承重墙在存储层。
三、原则二:单一权威来源——别让每个模块各判一套
同一条约束,若网关判一次、作业提交判一次、引擎侧再判一次,各写各的,迭代几轮后一定漂移成互相矛盾——矛盾处就是缝隙。
正确做法:每条横切约束只有一个权威判定组件,其余模块引用它。「我的数据空间」里,权限判定以 ACL 真值表为唯一口径,重建存储策略、判断可读可写,全部从这一张表推导;哪些数据源可以被引擎直查,由目录类型定义作唯一真值,前端、后端、引擎同引一处。改约束时,权威源与所有引用处一起改。
产品形态上也一样:成员角色、访问令牌、系统账号凭据托管收在同一处空间管理,授权湖表时存储侧策略联动下发——上层权限与底层存储权限永远同源:
四、原则三:fail-closed——功能可以降级,安全不能
一个功能因解析失败而不可用,用户会抱怨,但数据不会泄露;一个安全边界因解析失败被跳过,用户毫无感知,数据已经出去了。两者的代价完全不对等,所以异常分支的默认动作必须是保守拒绝:
- 解析失败不等于安全通过:解析不出涉及哪些表的语句,默认拒,而不是「放行让引擎自己说」;
- 缺关键上下文即拒:引擎会话没带可信的空间标识,就拒绝建立会话,而不是当作没有限制;
- 成员校验前置:「是不是这个空间的成员」在所有路径最前面无条件执行,不藏在「解析成功」的分支里;
- 身份令牌兜底:令牌识别异常时,退到能力最窄的身份,而不是最宽。
还有一条职责归属:SQL 语法与执行的权威是引擎,不是网关的尽力解析。 网关解析只做尽力拦截,绝不作唯一鉴权闸——两个解析器对同一条语句的理解一定有分歧,分歧处就是绕过点。真正的强制要么落在引擎侧,要么沉到存储层。
五、原则四:凭证不落盘
进程的启动参数、命令行都能被读出来;编码不是加密。凭证的正确到达方式是运行时懒加载:作业启动时不预置任何存储或数据库凭证,只带一把一次性令牌;要访问数据时凭令牌回调平台内部接口现取,凭证只驻进程内存,不进命令行、不进配置快照。令牌签发即冻结归属空间的能力集,运行中的作业无法给自己提权;即便泄露,半径也限定在单个作业、单个空间。
六、两条通用提醒
- 表级鉴权天然拦不住「不产生表目标」的操作(反射调用、加载自定义函数)。要么明确拒掉这类原语,更根本的是执行身份换成空间小账号——逃逸半径收敛在本租户;
- 删表横跨元数据与数据两个平面,每个平面都要有唯一的拦截点,只守一边一定有绕过路径;底层存储自带原生权限机制的,尽量借力,别在上层重造一套判断。
收束:一道自检题
四条原则没有一条在讲「怎么加权限模块」:数据面隔离决定承重墙沉在存储层还是飘在网关;单一权威来源决定约束会不会随迭代漂移;fail-closed 决定异常时倒向拒绝还是放行;凭证不落盘决定攻击面里有没有明文密钥。
最后一道自检题:假设所有应用层鉴权都被绕过——用户直连引擎、跑起任意代码——还有什么拦住他跨租户读数据? 答案应该是存储层。这正是「我的数据空间」(datastudiohappy.cn)把隔离沉到数据面的原因。
这套权限体系是「我的数据空间」的一部分——一套可私有化部署的数据平台,支持 OEM 合作。产品介绍:datastudiohappy.cn/,交流合作 QQ:1559851993。