惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
Martin Fowler
Martin Fowler
MyScale Blog
MyScale Blog
The GitHub Blog
The GitHub Blog
I
InfoQ
A
About on SuperTechFans
GbyAI
GbyAI
宝玉的分享
宝玉的分享
爱范儿
爱范儿
博客园 - 【当耐特】
博客园 - 司徒正美
博客园 - 聂微东
P
Proofpoint News Feed
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
Last Week in AI
Last Week in AI
阮一峰的网络日志
阮一峰的网络日志
B
Blog RSS Feed
Jina AI
Jina AI
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
博客园 - 叶小钗

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计
【身份与访问控制工程】B2B SaaS 多租户权限设计
Liao Tonglang · 2026-04-21 · via 土法炼钢兴趣小组的算法知识备份

单租户的权限问题已经够复杂了(见 RBAC/ABAC/ReBAC 选型)。多租户额外引入了一个维度:同一个 SaaS 平台上有 100 个公司,每个公司内部有自己的角色体系、组织结构和审批规则。这意味着你需要在同一个数据库里同时运行 100 套互相隔离的权限系统。

一、租户隔离:物理 vs 逻辑

多租户的第一个决策是隔离级别:

隔离方式 数据层 认证层 授权复杂度 成本
单 DB + tenant_id 字段 逻辑隔离(WHERE tenant_id = ?) 共享 IdP 高(所有租户共用同一套权限表)
DB per Tenant 物理隔离(独立数据库) 可共享或独立 IdP 低(每个租户有独立的权限表)
Schema per Tenant PostgreSQL Schema 级别隔离 共享 IdP

绝大多数 B2B SaaS 选择第一种方案(单 DB + tenant_id),因为它在工程和运维复杂度之间取得了平衡。但要付出额外的代价:每个涉及权限的 SQL 查询都要带 WHERE tenant_id = ?,忘记加这个条件就会造成跨租户数据泄露。

工程陷阱 1:在”单 DB + tenant_id”模式下,大多数跨租户数据泄露漏洞不是来自”恶意攻击者精心构造请求”,而是来自”新加入的后端工程师在 JOIN 查询中忘了加 WHERE tenant_id = ?“。防御手段是:应用层的 ORM 自动注入 tenant_id 过滤器(如 Hibernate 的 @Filter、Django 的 Row-level security),或者数据库层的 RLS(Row-Level Security,PostgreSQL 14+ 支持)。

二、多租户权限的四层架构

flowchart TD
  L1["第一层:平台级权限<br/>(Platform Admin, Billing Admin)"]
  L2["第二层:租户级功能权限<br/>(租户自定义角色: Admin, Manager, Viewer, ...)"]
  L3["第三层:组织/部门级数据权限<br/>(只能看本部门和下属部门的数据)"]
  L4["第四层:资源级协作权限<br/>(谁能看这个文档/这个项目)"]

  L1 --> L2 --> L3 --> L4

2.1 第一层:平台级权限

平台运营团队需要的权限——管理所有租户、查看所有 billing 信息、介入客户支持。这层不与租户内部权限重叠,通常用全局 RBAC 角色(Platform Admin、Ops、Support)实现。

2.2 第二层:租户级功能权限

这是 RBAC 在租户维度的扩展——每个租户可能有自己的角色定义。

数据结构方案:

-- 全局角色表(平台预定义的角色模板)
CREATE TABLE roles (
    id          UUID PRIMARY KEY,
    tenant_id   UUID,  -- NULL = 平台角色(跨租户共享), NOT NULL = 租户自定义
    name        VARCHAR(128),
    permissions JSONB,  -- ["user.read", "user.write", "report.export"]
    UNIQUE (tenant_id, name)
);

-- 用户-角色分配
CREATE TABLE user_roles (
    user_id     UUID,
    role_id     UUID REFERENCES roles(id),
    tenant_id   UUID,  -- 冗余字段用于快速筛选
    PRIMARY KEY (user_id, role_id)
);

租户自定义角色通过 tenant_id 隔离——租户 A 的 “Custom-Admin” 角色和租户 B 的 “Custom-Admin” 角色是两条不同的记录,带不同的权限。

2.3 第三层:组织树与数据权限

这是多租户权限中最复杂的部分。一个典型的 B2B 场景:

公司 A:
  ├── 总部 (Department)
  │   ├── 工程部
  │   │   ├── 平台组 (Team)
  │   │   └── 产品组
  │   └── 市场部
  └── 分部 (Department)
      └── 客户支持

需求:平台组的 Manager 应能看到本组和下属组(如果有)的数据,
      同时能看到工程部的汇总数据,但看不到市场部的数据。

这种需要的是”组织树 + 作用域”模型:

CREATE TABLE org_nodes (
    id          UUID PRIMARY KEY,
    tenant_id   UUID NOT NULL,
    parent_id   UUID REFERENCES org_nodes(id),  -- 树结构
    name        VARCHAR(255),
    type        VARCHAR(64)  -- 'department', 'team', 'division'
);

-- 用户角色在特定作用域中生效
CREATE TABLE user_role_scopes (
    user_id     UUID,
    role_id     UUID,
    org_node_id UUID,  -- 该角色在哪个组织范围内生效
    tenant_id   UUID,
    PRIMARY KEY (user_id, role_id, org_node_id)
);

授权检查时,需要从用户当前组织节点出发,向上遍历祖先,收集所有 user_role_scopes 中生效的权限。如果用户是”工程部”的 Manager,在查询”平台组”的数据时,授权检查应沿组织树上溯,判断”工程部”的 Manager 角色是否覆盖”平台组”。

工程陷阱 2:组织树的深度通常不大(5-8 层),但每个授权检查都要做一次图遍历(从叶子节点上溯到根)。如果权限查询频繁(如每个 API 请求都需要),需要做组织树缓存——预计算每个用户的”有效组织范围列表”,缓存到 Redis,在组织变更时失效。

2.4 第四层:资源级权限(Zanzibar 风格)

文档、项目、文件夹的共享和协作权限适合用 Zanzibar 模型(见上一篇文章)。在多租户场景中,Zanzibar 的 namespace 需要是 tenant-aware 的——不同租户的 relation tuple 完全隔离:

# Zanzibar 式 relation tuple,带 tenant 前缀
⟨tenant:a/doc:readme.txt #viewer user:zhangsan⟩
⟨tenant:a/group:eng-leads #member user:lisi⟩

# 而非
⟨doc:readme.txt #viewer tenant:a/user:zhangsan⟩

前者保证了不同租户之间的关系元组在物理索引上隔离,避免跨租户的图遍历。

三、跨租户授权

有些场景中,一个租户的用户需要访问另一个租户的数据。典型场景:

  • 第三方服务商:某会计事务所是 SaaS 平台的客户之一,“审计师”角色需要查看其客户的财务数据(这些客户也是平台的租户)。
  • 企业集团:母公司租户的管理员需要查看子公司租户的数据。

跨租户授权的架构选择:

  1. 委托授权(Delegated Access):租户 A 在界面上选择”允许租户 B 的用户 X 访问我的 Y 数据”。生成一条跨租户的 relation tuple。
  2. 父租户模型:在租户之间建立父子关系,父租户的管理员自动获得子租户的指定权限。
  3. 代理账号(Service Account per Tenant):为第三方创建目标租户下的服务账号,限定权限范围。

四、小结

多租户权限不是”单租户权限 × N”。它是一个新的维度,会和你已有的 RBAC、组织树、Zanzibar 风格的资源级权限互相交叉:

  1. 首先选好租户隔离模型(单 DB + tenant_id 是多数 SaaS 的起点)。
  2. 四层权限架构(平台级→功能级→数据级→资源级)作为设计蓝图,每层有独立的实现方式。
  3. 组织树缓存是性能关键——预计算用户的”有效组织范围”。
  4. ID 被遗忘的 WHERE tenant_id 是跨租户数据泄露的首要原因——从第 1 天就在 App 层建立防呆机制。

上一篇OPA、Cedar 与策略引擎落地 下一篇API Gateway、BFF 与边界认证授权

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-13 · architecture / security

【身份与访问控制工程】IAM 全景:为什么这是高价值赛道

从 2020 年 SolarWinds 到 2024 年 Okta 支持系统泄露,身份基础设施的安全失败反复证明一件事:IAM 不是 IT 支撑系统,而是安全架构的承重墙。本文建立现代 IAM 的全景地图——从认证协议、令牌体系、权限模型到身份治理与平台选型,给出 5 个贯穿全系列的核心问题。

2026-06-17 · architecture / security

【身份与访问控制工程】RBAC、ABAC、ReBAC:权限模型怎么选

RBAC 简单但会角色爆炸,ABAC 灵活但策略管理失控时更可怕,ReBAC 表达力强但引入了图遍历的性能约束。三种模型不是'选一个升级另一个'的线性关系,而是在表达能力、管理成本和性能三者之间做工程权衡。本文从每种模型的本质数据结构出发,拆解选型框架。

2026-06-18 · architecture / security

【身份与访问控制工程】Zanzibar 风格权限系统:Google 的全球授权引擎

Google Zanzibar 论文在 2019 年发布后,引发了开源授权系统的一波重新设计:Auth0 FGA、SpiceDB、Permify、Ory Keto——全都基于 Zanzibar 的'关系图+命名空间配置'模型。但论文本身只讲了 What,没深入 Why。本文从 Zanzibar 的 relation tuple 模型、namespace config 的语义、consistency 模型(Zookie)和工程权衡出发,拆解为什么 Zanzibar 的设计决策是这样的,以及你自己实现时要面对什么。

2026-06-18 · architecture / security

【身份与访问控制工程】OPA、Cedar 与策略引擎落地

OPA 是 CNCF 的策略引擎标准答案,Rego 是它的策略语言;Cedar 是 AWS 开源的新竞争者,基于 Rust 的 WASM 编译执行、语法更接近 SQL。两者在架构模式(sidecar vs 中心化)、策略语言设计哲学和性能特征上有根本差异。本文从策略引擎的架构模式出发,拆解 OPA Rego 的核心语义与性能限制、Cedar 的设计取舍,以及策略即代码(Policy as Code)在 CI/CD 中的落地。