rls在sql server 2016中仅支持绑定到基表,不支持视图;策略通过基表生效,视图仅透传,create security policy语法强制要求on后为物理表,指定视图会报错“对象不是表”。

Row-Level Security(RLS)在 SQL Server 2016 中不支持直接作用于视图,只对基表(base table)生效。这是最常被误用的点——你不能给视图加安全策略,哪怕它只查一张表。
如果你看到“对视图启用 RLS”的说法,背后实际是:
- 视图所依赖的底层表已启用 RLS;
- 查询该视图时,RLS 谓词自动生效(因为查询最终落在基表上);
- 但你无法在
CREATE SECURITY POLICY中指定视图名作为ON目标。
CREATE SECURITY POLICY 只接受基表,不接受视图
SQL Server 的安全策略语法强制要求目标为物理表:
CREATE SECURITY POLICY SalesFilterPolicy
ADD FILTER PREDICATE dbo.fn_securitypredicate(SalesRep) ON dbo.Sales -- ✅ 合法:dbo.Sales 是表
-- ADD FILTER PREDICATE dbo.fn_securitypredicate(SalesRep) ON dbo.vw_Sales -- ❌ 报错:视图不被允许
错误信息会是:
The object 'vw_Sales' is not a table.
- RLS 策略绑定发生在查询编译阶段,引擎需要能确定行的物理归属;
- 视图没有存储行,也没有
sys.dm_db_partition_stats或sys.partitions元数据支撑谓词注入; - 即使是单表视图(
SELECT * FROM Sales),SQL Server 也不会将其“降级”为等价表来应用策略。
如何让视图“看起来”有行级安全?
真正起作用的是基表 + 用户上下文 + 谓词函数,视图只是透传层。你需要:
- 确保视图查询的基表已启用 RLS;
- 谓词函数中使用
USER_NAME()、SUSER_SNAME()或SESSION_CONTEXT()获取调用者身份; - 避免在视图定义里做
WHERE过滤(否则可能绕过或干扰 RLS); - 测试时必须用真实数据库用户连接(不是
sa或拥有UNMASK/ALTER ANY SECURITY POLICY权限的账号),否则策略会被跳过。
示例关键点:
-
fn_securitypredicate必须是内联表值函数(ITVF),不能是多语句 TVF; - 函数里不要调用非确定性函数(如
GETDATE())做主过滤逻辑,否则可能被优化器剔除; - 如果视图涉及多表
JOIN,RLS 仅对显式声明了策略的那张表生效,其余表不受影响。
容易踩的坑:权限与上下文混淆
-
EXECUTE权限对谓词函数是必需的,但很多人只给了SELECT忘了这个; - 使用
WITHOUT LOGIN创建的测试用户(如Sales1)可以触发 RLS,但若用 Windows 账户登录,USER_NAME()返回的是数据库用户名,不是域账户名,需确保映射正确; - 如果你在视图里用了
WITH SCHEMABINDING,而基表后来加了 RLS 策略,不会报错,但策略仍只作用于基表——这点常让人误以为“没生效”。
RLS 的生效完全依赖查询计划是否包含对目标表的访问,以及执行时是否满足谓词条件。视图本身没有任何策略容器能力,它只是语法糖。










