本文目录
多租户改造不是给用户表加 tenant_id,而是让身份、数据、任务、缓存和运维入口共享同一隔离契约
本文面向准备把旧 ERP、CRM、订单平台或行业系统升级为 SaaS 的产品、研发、安全与运维负责人。先定义租户生命周期和可信租户上下文,盘点数据库、对象存储、缓存、搜索、消息、报表、定时任务与第三方凭据中的数据归属;再选择独立环境、独立库或 Schema、共享表等隔离模型。迁移时按租户建立可审计映射,先补齐归属、再强制访问约束,逐租户切流并验证回退。登录成功不等于租户隔离成立;所有读取、写入、导出、后台任务、智能体工具和运维操作都必须按服务端确认的租户边界执行。
按隔离要求、运维能力与成本选择租户模型
| 候选模型 | 适用条件 | 主要风险 |
|---|---|---|
| 租户独立环境或独立库 | 隔离或合规要求高,客户愿意承担单独资源成本,团队具备统一部署和升级能力 | 版本漂移、资源利用率低、备份监控和结构升级随租户数放大 |
| 共享实例、独立 Schema | 需要较清晰的数据边界,同时希望复用数据库基础设施并可逐租户迁移 | 动态表名或搜索路径处理错误,连接复用后上下文残留造成串租户 |
| 共享表、租户字段 | 租户数量多、结构一致,应用与数据库访问层能够强制携带并校验租户键 | 漏写一个过滤条件、缓存键或后台任务条件就可能跨租户访问 |
| 混合模型 | 多数租户共享,少数高合规或高负载租户独立,统一数据访问层能隐藏物理位置 | 路由、迁移、统计和故障处置变复杂,独立租户容易形成一次性分支 |
适用边界:先定义什么是租户,以及隔离要覆盖到哪里
本文所说的租户是具有独立数据边界、成员关系、配置和生命周期的客户组织或业务主体,不等同于部门、角色、门店或普通用户。若只是同一企业内部按岗位控制菜单与数据范围,应优先完善业务授权;只有确实存在彼此不能访问数据的客户边界,才进入多租户改造。
先写清租户创建、启用、暂停、合并、拆分、迁出和删除由谁批准,用户能否属于多个租户,以及同一自然人在不同租户内的身份和角色如何区分。平台运营人员、客服、实施人员和机器账号也必须纳入模型,不能用一个全局管理员绕过所有隔离规则。租户隔离与身份认证、角色授权、数据加密是不同控制面,需要组合验收。
隔离范围不只在关系数据库。对象存储目录、缓存、搜索索引、消息主题、导出文件、日志、监控标签、第三方 API 凭据、定时任务和 AI 智能体知识库都可能保存或处理租户数据。某层选择共享并不表示其他层也必须共享;可按合规、性能、成本和团队运维能力选择独立、共享或混合模型,并记录每层的边界与责任。
- 定义租户、用户、角色、组织和平台人员之间的关系。
- 按存储、计算、缓存、消息、文件、搜索与外部凭据盘点隔离范围。
- 写明本期不迁移的客户、数据和入口,以及对应风险与补救。
从可信租户上下文开始,不能相信客户端传来的 tenant_id
请求中的租户必须由服务端根据已验证身份、会话、域名、客户端注册或受控路由确定,并检查该主体确实属于目标租户。前端隐藏字段、URL 参数和请求头可以承载路由提示,但不能单独作为授权依据。
建立统一租户上下文,至少包含稳定租户标识、用户或服务主体、成员关系、当前角色、客户端、请求标识和必要的策略版本;在网关或认证层完成初步解析后,业务服务仍按自身资源和动作验证。用户可切换租户时,每次切换都重新确认成员资格,缓存和后台任务不得沿用上一个租户上下文。OWASP 的授权建议强调默认拒绝并在每次请求验证权限,这同样适用于租户边界。
盘点所有没有用户请求上下文的入口:消息消费者、定时任务、Webhook、批量导入、报表、客服工具、数据修复脚本和智能体工具调用。它们应携带不可歧义的租户标识和最小权限服务身份,无法确定归属时进入隔离队列或人工复核,不允许回退为“查询全部租户”。日志记录租户标识与判定来源,但不记录令牌、密码或不必要的个人信息。
- 租户上下文由服务端验证生成,客户端值只能作为待校验输入。
- 同步请求、异步事件、后台任务和人工运维使用同一租户契约。
- 上下文缺失、冲突或成员失效时默认拒绝并留下可审计原因。
选择独立、共享或混合模型时,同时评估隔离与运营成本
AWS 的 SaaS 资料将常见数据分区概括为 silo、bridge 与 pool:从租户独立资源,到共享实例但分开表示,再到共享表并用租户键分区。三种模型都可能合理,选择应来自客户要求、故障影响、数据规模、升级频率和团队自动化能力。
独立环境或数据库形成更直观的资源边界,也便于单租户备份、恢复和性能调优,但会放大配置、监控、补丁、结构升级与成本管理工作;如果需要人工逐个维护,租户增长后会形成版本漂移。共享表便于统一发布和资源利用,却要求每个访问路径都正确携带租户键,并治理一个租户影响其他租户的“噪声邻居”、共同故障域和资源计量。
混合模型可以让多数租户共享,少数有合规或高负载要求的租户独立,但应用不应在业务代码中到处判断物理位置。使用统一数据访问与路由层,把租户映射到资源,保留路由版本和审计,同时让备份、迁移、报表与故障手册都理解这种映射。不能为了销售承诺给每个独立租户维护不同代码分支;隔离方式可不同,产品版本和发布治理仍应统一。
- 逐层记录独立、共享或混合选择及对应客户与合规依据。
- 比较部署升级、备份恢复、监控、容量和单租户迁移的长期成本。
- 物理位置由统一路由管理,不暴露给客户端或散落在业务条件中。
存量数据先建立归属证据,无法确定的记录不得自动并入默认租户
旧系统中的公司名称、门店名、创建人或来源渠道可能缺失、变更或重复,不能直接当作稳定租户键。迁移需要为每类业务对象找到可追溯的归属证据,并把冲突、孤儿记录和跨客户共享对象单独处理。
先建立稳定租户主表与旧客户到新租户的映射,再按订单、合同、客户、附件、库存、审批、消息和审计记录的业务关联传播租户归属。对同一对象关联多个客户、没有上级对象、已删除来源或人工共用数据的情况输出异常清单,由业务负责人决定拆分、复制、设为平台公共数据还是只读归档。不要为了让脚本跑通,把所有未知记录填到一个默认租户。
回填按稳定主键小批执行,保存数据水位、映射版本、成功、冲突和待复核数量;重复执行不得改变已经人工确认的归属。迁移期间的新写入必须由服务端生成租户标识,不能继续产生无归属数据。完成后按租户核对对象数量、金额、状态、关联、附件和权限范围,同时检查同一业务键是否被错误分配给多个租户。
- 租户主键不可复用,显示名称与域名只作为可变属性。
- 未知、冲突、公共和跨租户对象分别进入明确处理流程。
- 回填可暂停续跑,并按租户与业务对象完成规则化对账。
把租户隔离落实到查询、缓存、文件、消息和数据库防线
共享表中的每个租户对象要有不可为空的租户键,唯一性、关联关系和常用索引通常也要包含租户维度。业务层集中构造受租户约束的数据访问接口,禁止普通代码获得无边界仓储或原始数据库连接。
读取、更新和删除都同时核对资源标识与租户标识,不能先按全局 ID 查出对象再只在界面隐藏。缓存键、对象存储路径、搜索文档、消息主题或消息体、导出文件名和临时任务也要绑定租户;后台批量操作显式遍历授权租户,不使用遗漏条件的全局查询。上传下载使用服务端授权或短时受控地址,不能因为知道对象路径就访问其他租户文件。
PostgreSQL 当前文档说明启用行级安全后,普通访问必须由策略允许,无策略时默认拒绝;但表所有者通常绕过策略,TRUNCATE 和 REFERENCES 等整表操作也不受其约束。因此 RLS 可作为共享表的纵深防线,却不能替代应用授权、连接角色治理和运维限制。若使用会话变量传递租户,要验证连接池复用、事务边界和异常退出后上下文已清除。其他数据库应按其官方能力设计,不可假设同样语义。
- 数据、缓存、文件、搜索和消息键都包含经验证的租户边界。
- 普通应用角色不能绕过隔离策略,平台操作使用单独审批与审计路径。
- 数据库策略、ORM 默认作用域与应用授权分别测试,避免单点失效。
实施步骤:先影子校验,再按租户切流并保留回退位置
改造期间旧入口可能仍按单客户或弱隔离方式工作,新入口则强制租户上下文。两套路径长期并存会扩大风险,因此应把共存限制在明确窗口,并以租户为最小切换和回退单元。
第一步冻结租户定义和数据模型,增加不含秘密的访问审计;第二步建立租户主表、映射和上下文传播;第三步让新写入同时满足归属规则,在不改变用户结果的影子模式下比较旧查询与受租户约束查询;第四步选择内部或低风险租户,迁移数据、缓存、文件、搜索与任务配置;第五步把该租户入口切到新路径并监控差异、拒绝和资源;第六步逐租户扩大,最后关闭无租户上下文的旧入口与全局凭据。
回退不能删除已经确认的租户归属,也不能让新产生的数据重新变成全局可见。某租户切换失败时,可把其路由退回仍能理解租户字段的兼容版本,或暂停该租户写入后修复;不得为了恢复一个客户而关闭所有租户过滤。迁出独立环境或迁回共享池时,仍需执行全量、增量、对账和最终切换,不能只改一条路由映射。
- 每个租户有迁移水位、差异、入口路由、继续或回退决定记录。
- 影子查询不向用户返回结果,也不产生业务副作用。
- 旧入口、无租户任务和全局凭据都有最后接受日期与退役证据。
验收:用两个以上租户证明不会串数据,也不会互相拖垮
只验证一个租户的正常流程无法发现隔离缺口。测试矩阵至少包含两个具有相似对象 ID 的租户、平台人员、跨租户成员、停用租户、后台服务身份和未知租户,并覆盖所有读取与写入入口。
对列表、详情、搜索、导出、附件、消息、通知、报表、Webhook、批量任务、客服工具和 AI 智能体逐项执行:用租户 A 身份尝试访问租户 B 的对象标识、缓存键、文件地址和任务结果,期望得到不泄露对象存在性的受控拒绝;篡改请求中的租户值、切换租户后复用旧会话、并发请求复用连接,以及重放异步消息,都应保持边界。授权拒绝也要进入审计,但响应和日志不能泄露他租户敏感数据。
隔离还包括资源影响。对共享模型按租户观察请求、并发、队列、存储和费用,演练单个租户的批量导出、热点查询或错误重试,确认配额、限流和任务调度不会让其他租户失去服务。AWS 的 pooled model 资料明确提示噪声邻居、共同故障域与租户成本归因问题;验收报告应说明仍共享的资源、最大影响范围和需要独立部署的条件。未能证明任一入口的租户来源、数据归属或拒绝行为时,应停止切流并明确为未完成。
- 使用相同对象 ID 的多租户负向测试覆盖所有入口和异步链路。
- 备份恢复、租户迁出与删除验证不会带出其他租户数据。
- 隔离正确性、性能配额、审计和运维恢复共同通过验收。
需求讨论中的问题
所有表加上 tenant_id,就算完成多租户隔离了吗?
不算。还要验证租户上下文来源、每次请求授权、关联与唯一约束、缓存、文件、搜索、消息、导出、任务、运维入口和资源配额。任何一条遗漏路径都可能跨租户访问。
独立数据库一定比共享表安全吗?
边界更直观,但仍依赖正确路由、凭据、备份、运维权限和统一升级。连接到错误数据库、共用高权限账号或导出文件混放仍会泄露;选择应结合隔离要求、自动化能力和成本。
可以直接使用请求头里的 tenant_id 查询数据吗?
不可以把它当成唯一依据。服务端必须从已验证身份和成员关系确认当前租户,再把可信上下文传给后续服务;客户端字段只能作为待验证的路由提示。
PostgreSQL 行级安全能替代应用权限检查吗?
不能。RLS 适合作为纵深防线,但表所有者和部分整表操作有不同语义,连接角色与会话上下文也可能配置错误。应用仍需按资源和动作做授权,并单独治理运维入口。
旧系统多租户改造上线验收清单
可逐项勾选,辅助本次阅读;刷新页面后不保留。
参考资料
技术概念可进一步阅读以下资料;文中的业务场景与实施清单用于方案讨论。
- AWS · SaaS partitioning models
核对 silo、bridge、pool 三类租户数据分区模型及其边界;具体云服务实现、合规与成本需按项目确认。
- AWS · Pool isolation
核对共享资源模型在效率之外还需处理噪声邻居、共同故障域、成本归因与合规取舍。
- PostgreSQL Documentation · Row Security Policies
核对行级安全的策略、默认拒绝、表所有者绕过与不适用操作;仅作为特定数据库的纵深防线参考。
- OWASP Cheat Sheet Series · Authorization
核对默认拒绝、每次请求验证权限与访问控制测试原则;具体租户模型仍需结合业务资源设计。
旧系统准备转成多租户,又担心客户数据串用?
提供现有客户部署方式、账号体系、数据来源、存储与后台任务清单,月弦科技可以协助拆分租户模型、迁移和隔离验收范围。