3、隔离原理:多租户隔离、数据隔离、环境隔离

隔离这件事,说白了就是「谁的数据归谁,谁的密钥归谁」。你想想看,一个系统里同时跑着几十家客户,要是A租户能看到B租户的账单,或者测试环境的密钥不小心配到了生产环境……那画面太美我不敢看。

我个人习惯把隔离拆成三个维度来设计:多租户隔离解决「人与人的数据不混」;数据隔离环境隔离解决「开发测试生产各玩各的」。三条线各守一摊,但又互相咬合。

下面我一个一个说,顺带聊聊我在项目里踩过的坑。

3.1 多租户隔离:你的数据就是你的

多租户隔离的核心问题就一个:怎么让不同租户的数据在同一套系统里和平共处。最常见的做法有三种,我直接拉个表对比:

方案 隔离粒度 运维成本 安全等级 适用场景
共享数据库 + 租户ID字段 行级 低 ★☆☆ 中小型SaaS、长尾客户
独立Schema(同一库) 表级 中 ★★☆ 中型客户、需要部分隔离
独立数据库 库级 高 ★★★ 金融、医疗、高合规场景

共享数据库+租户ID 是最省事的。每个表加个 tenant_id,查询时WHERE条件带上就行。但这里有个坑——只要有一条SQL忘记过滤,数据就串了。

我曾经在审计一个老项目时发现,某个统计报表的SQL里只查了 tenant_id = 1 的数据,但报表页面上显示的却是所有租户的汇总。原因是开发在写子查询时漏了租户过滤条件。这种bug用静态分析工具很难扫出来,必须靠行级安全策略兜底。

独立Schema 是我个人比较推荐的中庸方案。每个租户一个Schema,表结构相同,但数据物理隔离。查询时切换 search_path 就行。我在一个教育SaaS平台上用过这套,几百个租户跑得挺稳。

独立数据库 是终极方案。隔离最彻底,但运维成本也最高——连接池、迁移、备份都得乘以租户数量。我记得在做一个金融系统时,合规要求强制独立数据库,连数据库账号都不能复用。那段时间DBA团队天天加班……

我建议:如果你拿不准选哪种,先问自己两个问题——1)租户间数据是否允许被DBA看到?2)单个租户的数据量级会不会持续暴涨?两个「是」就上独立数据库,一个「是」就上独立Schema,全「否」再用共享表。

3.2 数据隔离:精细到字段级别

多租户隔离解决的是「租户与租户之间」的问题。但同一个租户内部,不同角色能看到的数据也不一样。这就是数据隔离要干的活。

数据隔离通常分三个层次:

  • 行级隔离——同一张表,不同用户只能看到属于自己的行。比如销售只能看自己的客户,经理才能看全团队。
  • 字段级隔离——同一行数据,不同用户看到的字段不同。比如HR能看到薪资,部门主管只能看到职级。
  • 加密隔离——敏感字段密文存储,即使用户有查询权限,没有密钥也解不开。

我重点说说字段级隔离,因为这是最容易出纰漏的地方。

有次在ERP项目中,财务角色打开员工信息页面时,意外看到了「薪资等级」字段——原本这个字段只对HR开放。排查下来发现,后端接口返回了全部字段,前端只是用v-if控制显示。这种「前端隔离」等于没隔离,懂点网络的人抓个包就破了。

正确的做法是:后端根据角色动态过滤字段,返回的数据里压根就不包含无权限的字段。前端只负责渲染。

行级隔离在SQL层面实现比较成熟。PostgreSQL的 Row-Level Security 可以直接在表上定义策略,不用改业务代码。MySQL 8.0 也有类似能力。我的建议是:能用数据库原生RLS就别自己写WHERE条件,数据库层面的隔离比应用层靠谱得多。

加密隔离一般在两种场景下用:一是身份证号、银行卡号等强敏感字段;二是跨环境传输时的脱敏需求。我个人习惯是在ORM层加一个透明加密注解,读时自动解密,写时自动加密——这样业务代码完全无感。

一个小经验:加密隔离一定要考虑「排序和模糊查询」的问题。全字段加密后就不能用 LIKE 了。折中方案是存一个加密字段用于精确匹配,再加一个脱敏字段用于模糊搜索。比如身份证存「加密密文 + 前6后4明文」。

3.3 环境隔离:密钥和配置别乱串

环境隔离解决的是「开发、测试、生产各玩各的」。听起来简单,但我见过太多把生产密钥配到测试环境,或者测试环境连了生产库的案例。

环境隔离的核心有三条:

  1. 密钥分离——每个环境独立签发密钥,不能复用。开发环境用自签名证书,生产环境用CA签发的证书。
  2. 配置隔离——数据库地址、API端点、日志级别等配置与环境绑定。我建议用配置中心(如Nacos、Consul)做环境命名空间,而不是靠配置文件里的profile来区分。
  3. 网络隔离——生产环境应该在其他环境无法直接访问的网段里。开发环境连不上生产库,这是基本原则。
我曾经因为测试环境和生产环境的Redis密码一样,导致测试脚本误操作清空了生产的缓存——那个周五晚上我到现在还记得。从那以后,我在所有项目里强制要求:每个环境的密钥必须由不同的人保管,且每次部署时自动校验密钥指纹。

还有一个容易忽略的点:日志中的密钥泄露。很多开发为了调试方便,会在日志里打印请求参数或数据库连接串。一旦这些日志被传到日志中心,等于把密钥公开了。我建议在日志采集端加一个脱敏过滤器,匹配到 password、secret、 等关键字时自动替换成 ***。

3.4 三种隔离如何协同

三种隔离不是各管各的,而是层层递进、互相补充的关系。多租户隔离划定了大边界,数据隔离在边界内做精细管控,环境隔离则保证这些规则在不同生命周期里一致执行。

下面这张图把整体架构串起来了:

权限隔离加密体系 · 三种隔离协同架构 多租户隔离 数据隔离 环境隔离 租户路由 Schema / 库隔离 连接池隔离 行级权限 (RLS) 字段级权限 加密存储隔离 密钥分离 配置命名空间 网络隔离 三种隔离相互独立又互为补充,共同构建完整的权限隔离加密体系 多租户隔离划边界 · 数据隔离做精细管控 · 环境隔离保执行一致

看到这张图的结构了吧?从上到下是「边界 → 精细 → 一致性」的递进关系。没有多租户隔离,数据隔离做得再细也白搭——因为A租户的数据可能直接跑到B租户的视野里。没有数据隔离,多租户隔离只是「能看见」和「看不见」的区别,同一个租户内的敏感字段照样裸奔。而环境隔离是托底的那层——它保证你在测试环境折腾时,不会把生产数据搞崩。

最后放一个三者对比的表格,方便你对照设计自己的隔离方案:

隔离维度 解决什么问题 核心手段 最容易踩的坑
多租户隔离 租户间数据不串 独立数据库/Schema/租户ID SQL漏写租户过滤条件
数据隔离 同一租户内权限分级 RLS、字段过滤、加密存储 前端隔离代替后端隔离
环境隔离 开发测试生产互不影响 密钥分离、配置命名空间、网络隔离 跨环境复用密钥或配置

嗯,三种隔离的原理就这些。说起来不复杂,但每一条我都拿真金白银的故障换过经验。你设计系统时把这三层想清楚,权限隔离这块的基本盘就稳了。