SQL Server 2019跨库存储过程EXECUTE AS执行失败求助
问题
在同一SQL Server 2019实例中,需通过WorkingDB数据库内的存储过程查询SourceDB的数据。以自身身份执行存储过程正常,但三处使用EXECUTE AS的尝试均失败,报错如下:
- 无法执行服务器主体,因为主体“Domain\ServiceAcctIn_GroupName”不存在、无法模拟或无权限。
- 以OWNER身份执行时:服务器主体“saName”无法在当前安全上下文下访问数据库“SourceDB”。
环境配置:
- 处于Windows AD域环境
- 服务账号所属组已获得SQL Server登录权限,且在两个数据库中均创建了对应的数据库用户
- 已将WorkingDB和SourceDB的
DB_CHAINING设为ON
核心需求:
- 必须使用服务账号查询SourceDB数据
- 不能将所有用户加入服务账号所在组
- 用户将通过另一域组获得WorkingDB中该存储过程的SELECT和EXECUTE权限
解决方案
问题根源分析
- 直接使用
EXECUTE AS LOGIN = 'Domain\ServiceAcctIn_GroupName'失败:SQL Server仅支持模拟单个Windows用户或SQL登录,无法模拟AD组,这是权限模拟的核心限制。 - 使用
EXECUTE AS OWNER失败:存储过程所有者为saName,但saName在SourceDB中未配置对应的数据库用户或访问权限,导致跨库访问的安全上下文断裂。
修正步骤
配置单个服务账号的权限:
- 为服务账号(单个AD用户,而非组)创建SQL Server登录
- 在SourceDB中创建该账号的数据库用户,并赋予
db_datareader或对应只读权限 - 在WorkingDB中创建该账号的数据库用户,确保其拥有存储过程的执行基础权限
正确设置存储过程的模拟身份:
- 使用
WITH EXECUTE AS指定单个服务账号对应的数据库用户,而非AD组或OWNER - 或在存储过程内部使用
EXECUTE AS USER(需确保目标是数据库用户)
- 使用
授予必要的模拟权限:
- 对需要执行存储过程的用户/域组,显式授予
IMPERSONATE目标服务账号数据库用户的权限
- 对需要执行存储过程的用户/域组,显式授予
修正后的代码示例
USE master; ALTER DATABASE [WorkingDB] SET DB_CHAINING ON; ALTER DATABASE [SourceDB] SET DB_CHAINING ON; GO -- 创建单个服务账号的SQL登录 IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = 'Domain\ServiceAcctIn') CREATE LOGIN [Domain\ServiceAcctIn] FROM WINDOWS; GO USE [SourceDB]; GO EXEC sp_changedbowner 'saName'; -- 为服务账号配置SourceDB访问权限 IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'Domain\ServiceAcctIn') CREATE USER [Domain\ServiceAcctIn] FOR LOGIN [Domain\ServiceAcctIn]; GRANT CONNECT TO [Domain\ServiceAcctIn]; ALTER ROLE db_datareader ADD MEMBER [Domain\ServiceAcctIn]; GO USE [WorkingDB]; GO EXEC sp_changedbowner 'saName'; -- 为服务账号创建WorkingDB用户 IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'Domain\ServiceAcctIn') CREATE USER [Domain\ServiceAcctIn] FOR LOGIN [Domain\ServiceAcctIn]; -- 创建终端用户组的数据库用户(用于授予存储过程权限) IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'Domain\EndUserGroup') CREATE USER [Domain\EndUserGroup] FOR LOGIN [Domain\EndUserGroup]; -- 创建带模拟身份的存储过程 CREATE OR ALTER PROCEDURE MySchema.GetDataFromSourceDB WITH EXECUTE AS N'Domain\ServiceAcctIn' -- 指定单个服务账号用户 AS BEGIN SELECT TOP(10) * FROM [SourceDB].[dbo].[TableName]; END GO -- 授予终端用户组存储过程执行权限 GRANT EXECUTE ON MySchema.GetDataFromSourceDB TO [Domain\EndUserGroup]; -- 授予终端用户组模拟服务账号的权限 GRANT IMPERSONATE ON USER::[Domain\ServiceAcctIn] TO [Domain\EndUserGroup]; GO -- 测试执行(使用终端用户组内的账号) EXEC MySchema.GetDataFromSourceDB;
关键注意事项
- 禁止尝试模拟AD组,仅能模拟单个Windows用户或SQL登录账号
- 确保
DB_CHAINING开启后,跨库访问的权限链未被中断 - 必须显式授予执行用户/组
IMPERSONATE目标用户的权限,否则模拟会失败
内容的提问来源于stack exchange,提问作者Paul Young
相关产品推荐
相关产品推荐

