工程工具知识花园

多空间权限:交集,不是并集

结论先行: 多空间应用的权限不能只看"用户是谁",必须先看"请求在哪个空间"。完整检查顺序固定为六步:解析空间、固定归属、计算组织动作、叠加项目角色求交集、提交时快照归属、服务端最终裁决。前端按钮的状态只是体验提示,服务端门禁、资源归属快照、跨空间数据隔离才是正式安全边界。

第一步:先解析请求属于哪个空间

权限计算的第一步不是查用户身份,而是解析请求的作用域:这个请求属于个人空间,还是某个团队空间。

  1. 请求必须显式声明空间;旧值只按兼容规则映射,非法值直接拒绝。
  2. 缺失时只能按明确的默认规则选择,不能脑补。
  3. 作用域、组织、项目、系列是四个不同层级的对象,不能用一个组织 ID 或一个全局角色替代。

把四个层级混成一层,是多空间越权最常见的起点。

第二步:归属一旦固定,请求体改写不了

  1. 个人空间的组织 ID 为空;团队空间绑定组织。
  2. 不能用请求体里的组织 ID 去覆盖已经解析出的作用域。
  3. 个人用户没加入团队,照样可以用个人空间;平台管理员也不能凭平台身份自动读取团队资源。

归属是解析出来的事实,不是客户端声称的事实。

第三步:能力是交集,只会缩小,不会放大

  1. 管理员、创建者、审核者、只读成员各自只有明确的模块动作集合,多一个动作都没有。
  2. 项目或系列的负责人、编辑、审核者、只读者,再与组织动作求交集:组织层面能生成,不代表在当前项目里能编辑。
  3. 只读成员可以查看允许的数据,但不能通过直达链接、键盘入口、GET 副作用、缓存键或异步任务绕过写入门禁。

交集是设计的本意:组织角色回答"能做什么动作",项目角色回答"在哪个项目里",两个答案取交之后,才是这个人在这一次请求里的真实能力。验收时要准备双组织测试数据:同一个人在两个组织里的动作集合可能完全不同,只测一个组织等于没测。

第四步:异步任务按提交时快照,不随空间切换改写

任务、生成记录和推送,保存的是提交那一刻的空间与组织快照。用户之后切换空间,已提交的任务归属不变,也不允许把任务跨空间查询。

没有快照的异步任务,等于把归属的决定权交给了"查询的那一刻",而那一刻的空间上下文可能是错的。

第五步:正式边界在服务端,不在界面

  1. 页面隐藏入口或置灰按钮,只是提示。直接调接口、媒体、任务、下载、导出,同样要过服务端门禁。
  2. 资源不存在时的 403 与 404 口径遵循接口契约,但响应、文件字节和缓存都不能泄露其他空间的资源。
  3. 浏览器刷新、前进后退、双账号同源会话、旧链接,必须纳入验收。只测"按钮是不是灰的",等于没测。

验收有一套固定顺序:先准备双组织测试数据,再按接口、页面、缓存、下载、导出的顺序逐个验收,最后补上浏览器刷新和双账号场景。顺序不能乱:接口门禁没过,页面按钮测得再漂亮也没用。

一个真实教训

一个视频工作台项目里曾经出现过一次越权:历史数据的组织 ID 为空,按兼容规则回退之后,团队空间看到了别人的项目。根因正是归属和解析混在了一层——旧数据没有空间快照,兼容映射又给了它一个合法身份。修复方向和上面的检查顺序完全一致:解析、固定归属、求交集、服务端裁决,一步都不能省。

小结

多空间权限就三句话:先看请求在哪,再看人是谁,最后取交集。界面的状态是做给人看的,服务端的门禁才是做给绕路的人看的。

© 2026 rotor®GitHub 开源CC BY-NC-ND · 转载请注明出处