SQL Server 2022行级安全自引用错误问题求助
修复方案
问题根因
SQL Server 2022的查询优化器对RLS predicate的循环依赖检测更严格:当访问User或UserRight表时,RLS会触发RLS.UserSameOrEditor函数,该函数调用HasEffectiveUserRole,后者又调用EffectiveUserRights访问UserRight表;同时CurrentUserId直接访问User表——而这两张表本身受RLS策略约束,形成间接循环引用,导致优化器在某些执行计划下抛出错误。
具体修复步骤
方案一:内联权限逻辑,消除函数链循环
直接修改RLS.UserSameOrEditor函数,跳过中间函数层,避免递归触发RLS:
ALTER FUNCTION [RLS].[UserSameOrEditor](@User int, @Subsystem int) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT 1 AS 'Allow' WHERE -- 当前登录用户直接匹配,允许访问 @User = (SELECT Id FROM dbo.[User] WHERE UserName = ORIGINAL_LOGIN()) -- 检查当前用户是否拥有指定子系统的编辑器权限(Role=8) OR EXISTS ( SELECT 1 FROM dbo.UserRight ur WHERE ur.[User] = (SELECT Id FROM dbo.[User] WHERE UserName = ORIGINAL_LOGIN()) AND ur.Subsystem = @Subsystem AND (ur.Rights & 8) = 8 ) )
方案二:拆分函数,解除SCHEMABINDING依赖
如果需要保留函数复用性,可将依赖表的函数改为无SCHEMABINDING(避免强制触发RLS约束检查):
- 重构
CurrentUserId函数:
DROP FUNCTION IF EXISTS [dbo].[CurrentUserId] GO CREATE FUNCTION [dbo].[CurrentUserId]() RETURNS int AS BEGIN DECLARE @Id int SELECT @Id = Id FROM dbo.[User] WHERE UserName = ORIGINAL_LOGIN() RETURN @Id END
- 修改
HasEffectiveUserRole函数,直接访问表而非调用EffectiveUserRights:
ALTER FUNCTION [dbo].[HasEffectiveUserRole](@Subsystem int, @Role int) RETURNS bit WITH SCHEMABINDING AS BEGIN DECLARE @Result bit, @UserId int SET @UserId = dbo.CurrentUserId() SELECT @Result = CASE WHEN (Rights & @Role) = @Role THEN 1 ELSE 0 END FROM dbo.UserRight ur WHERE ur.[User] = @UserId AND ur.Subsystem = @Subsystem RETURN COALESCE(@Result, 0) END
- 保留原
RLS.UserSameOrEditor函数即可。
临时应急方案(不推荐)
若上述重构暂时无法实施,可修改安全策略关闭SCHEMABINDING,但这会降低安全性(后续表结构变更可能破坏RLS逻辑):
ALTER SECURITY POLICY [RLS].[UserSecurity] WITH (SCHEMABINDING = OFF) GO
验证修复效果
修改完成后,重启SQL Server服务(清除执行计划缓存),进行长时间测试:
- 用普通用户登录,确认只能看到自身的用户信息和权限
- 用拥有编辑器权限(Role=8)的用户登录,确认能正常访问所有用户数据
内容的提问来源于stack exchange,提问作者Node
相关产品推荐
相关产品推荐

