不可行,因CURRENT_USER()返回数据库登录账户名而非业务租户ID;应使用会话变量:PostgreSQL用current_setting('app.tenant_id', true),SQL Server用SESSION_CONTEXT(N'tenant_id'),并由应用层预先设置。

视图里加 WHERE tenant_id = CURRENT_USER() 可行吗?
不行,CURRENT_USER() 返回的是数据库登录账户名,不是业务租户 ID。硬编码或直接用它做行过滤会导致所有用户看到同一份数据,或者权限错乱。真正可用的是会话变量或应用层传入的租户上下文。
用 SESSION_CONTEXT()(SQL Server)或 current_setting('app.tenant_id')(PostgreSQL)动态过滤
这是主流方案:应用在每次查询前,先设置会话级变量,视图再读取该变量做 WHERE 过滤。关键点在于变量必须可被视图访问,且不能被用户绕过。
- PostgreSQL 需提前执行
SET app.tenant_id = 't123';,视图中写WHERE tenant_id = current_setting('app.tenant_id', true);true表示失败时返回 NULL 而非报错 - SQL Server 用
SESSION_CONTEXT(N'tenant_id'),需先调sp_set_session_context设置,视图中用WHERE tenant_id = CAST(SESSION_CONTEXT(N'tenant_id') AS VARCHAR(50)) - MySQL 没原生会话变量透传机制,得靠
USER()解析或自定义函数(不推荐),更稳妥是改用代理层注入tenant_id到 SQL 中
视图定义里写死 tenant_id 参数会有什么问题?
视图本身不支持参数,所谓“参数化视图”只是伪需求。如果强行用字符串拼接(如 CREATE VIEW v_orders AS SELECT * FROM orders WHERE tenant_id = 'xxx'),那就退化成静态快照,每个租户都要建一个视图,维护爆炸式增长,且 DDL 权限暴露风险高。
- 100 个租户 → 100 个视图 → 100 次
CREATE VIEW,上线/下线租户要改结构 - DBA 或开发误查
v_orders_t123时漏带 WHERE,可能跨租户泄露 - 视图依赖的表加字段后,所有租户视图都要手动
ALTER,容易遗漏
为什么不能只靠视图 + 应用层 WHERE,而要强制走视图?
因为应用层可能漏写 WHERE tenant_id = ?,尤其在复杂 JOIN 或子查询场景。视图是唯一能从数据库侧兜底的手段——只要查视图,就不可能绕过租户过滤。
- 把基表
orders的 SELECT 权限收回,只开放给视图v_tenant_orders - 视图定义中必须包含
tenant_id = current_setting(...)且不可被OR、UNION等绕过(避免用OR tenant_id IS NULL) - 测试时用不同会话 SET 不同
tenant_id,直接查视图验证是否真隔离
最易忽略的一点:视图的执行计划是否仍能走 tenant_id 字段索引。如果会话变量导致优化器无法估算选择率,可能全表扫描——务必在真实数据量下 EXPLAIN 验证。

















