在多租户系统中,让不同客户的数据彼此不可见是最基本的安全要求。相对于独立库或独立表方案,基于WHERE条件的逻辑隔离把租户标识放在同一个表的行里,再通过SQL视图对外屏蔽非当前租户的记录。这种做法改动小、成本低,适合中小规模SaaS产品。

一、核心思路与视图定义
逻辑隔离的本质是:所有业务查询都自动带上tenant_id = 当前租户的条件。如果让开发人员在每条SQL里手写这个条件,既繁琐又容易遗漏。视图可以把这个条件固化在定义中,应用层只查询视图,而不是直接查询基表。
下面以订单表为例,创建只返回当前租户数据的视图。我们通过数据库会话变量来传递租户身份,这样视图定义无需为不同租户各写一份。
-- 基表 CREATE TABLE orders ( id BIGINT PRIMARY KEY, tenant_id INT NOT NULL, amount DECIMAL(10,2), created_at DATETIME ); -- 索引,加速租户过滤 CREATE INDEX idx_orders_tenant ON orders(tenant_id); -- 基于会话变量隔离的视图 CREATE VIEW v_orders AS SELECT id, tenant_id, amount, created_at FROM orders WHERE tenant_id = @current_tenant;
在上面的代码中,@current_tenant是MySQL的用户变量,其他数据库可用等效机制(如PostgreSQL的current_setting、SQL Server的SESSION_CONTEXT)。视图创建后,业务代码查询v_orders即可,不需要关心租户字段。
这种写法的优势在于隔离逻辑集中在一处。如果后续要调整隔离策略,例如加入软删除标记,只需修改视图定义,不用逐个改动业务SQL。同时,由于视图本身不存储数据,写入操作可借助INSTEAD OF触发器或只授予基表受控权限来完成。
二、会话级租户绑定与写入处理
仅查询视图还不够,必须保证每次连接使用时都正确设置了租户变量。通常在用户登录验证成功后,由数据访问层执行一条设置语句,将本次会话绑定到该用户的租户。
-- 登录后设置当前租户 SET @current_tenant = 12; -- 查询自动限定租户 SELECT * FROM v_orders WHERE amount > 100; -- 写入时仍需显式带租户,或借助触发器 INSERT INTO orders(id, tenant_id, amount, created_at) VALUES (101, @current_tenant, 199.00, NOW());
需要注意,视图默认不一定可写。若希望透过视图插入数据,可创建INSTEAD OF触发器(SQL Server)或在应用程序中统一封装写入方法,自动把tenant_id补上,避免视图写操作绕过隔离。
对于连接池场景,复用连接可能残留上一次会话的租户变量。因此每次从池中取出连接都要重新设置,或在查询前用存储过程封装。否则会出现A租户看到B租户数据的严重事故。
三、权限回收与防绕过
视图发挥作用的前提是应用账号不能直接访问基表。数据库管理员应仅授予视图的SELECT、有限INSERT权限,回收基表权限。
| 对象 | 授予权限 | 说明 |
|---|---|---|
| v_orders | SELECT, INSERT | 应用唯一可见的数据入口 |
| orders | 无 | 防止绕过视图直查全量 |
如果应用账号仍拥有基表权限,攻击者构造的SQL或代码漏洞就可能直接查询orders表而忽略tenant_id。因此权限设计要和视图配合使用,缺一不可。
另外,部分ORM框架在生成SQL时会自动拼表名,若配置错误也可能指向基表。建议在测试环境用审计日志检查实际执行的SQL,确认所有读操作都经过视图。
四、性能与索引配合
视图本身不提升性能,查询效率取决于底层基表索引。由于每次都带tenant_id等值条件,应在该列上建立索引,如前面示例的idx_orders_tenant。当表数据量达到千万级时,还可考虑复合索引(tenant_id, created_at)以覆盖常见排序。
另一个隐性开销是会话变量判断。在个别数据库中,视图里的函数或变量引用会阻止某些执行计划优化。此时可改用带有形参的表值函数,或利用行级安全策略(如PostgreSQL RLS)作为替代。但RLS与视图思路一致,都属于逻辑隔离范畴。
最后提醒,逻辑隔离不是加密隔离。具备数据库超级权限的人员仍能看到全部租户数据,所以还要配合审计、传输加密与备份访问控制,才能构成完整的安全闭环。
五、适用场景与局限
基于WHERE条件的视图隔离适合租户数量多但单租户数据量中等的场景,例如企业内部SaaS、轻量级CRM。它的部署成本很低,不需要分库分表中间件。
但当单租户数据极度膨胀,或合规要求物理隔离时,视图方案就力不从心了。此时应评估独立库或分区表。不过在多数早期项目里,先用视图隔离快速上线,后期再迁移,是风险可控的技术路线。