适用于:SQL Server
Azure SQL 数据库
Azure SQL 托管实例
在 SQL Server 中,可以定义以下用户定义模块的执行上下文:函数(内联表值函数除外)、过程、队列和触发器。
通过指定执行模块的上下文,可以控制 数据库引擎使用哪一个用户帐户来验证对模块引用的对象的权限。 这有助于人们更灵活、有力地管理用户定义的模块及其所引用对象所形成的对象链中的权限。 必须而且只需授予用户对模块自身的权限,而无需授予用户对被引用对象的显式权限。 只有运行模块的用户必须对模块访问的对象拥有权限。
语法
本部分介绍适用于 . 的 EXECUTE ASSQL Server 语法。
函数(内联表值函数除外)、存储过程和 DML 触发器:
{ EXEC | EXECUTE } AS { CALLER | SELF | OWNER | 'user_name' }
具有数据库范围的 DDL 触发器:
{ EXEC | EXECUTE } AS { CALLER | SELF | 'user_name' }
具有服务器范围和登录触发器的 DDL 触发器:
{ EXEC | EXECUTE } AS { CALLER | SELF | 'login_name' }
队列:
{ EXEC | EXECUTE } AS { SELF | OWNER | 'user_name' }
参数
CALLER
指定模块内的语句在模块调用方的上下文中执行。 执行模块的用户不仅必须对模块本身拥有适当的权限,还要对模块引用的任何数据库对象拥有适当权限。
CALLER 是除队列以外的所有模块的默认值,与 SQL Server 2005 (9.x) 行为相同。
CALLER不能在或CREATE QUEUEALTER QUEUE语句中指定。
SELF
EXECUTE AS SELF 等效于 EXECUTE AS <user_name>指定用户创建或更改模块的人员。 创建或修改模块的人员的实际用户 ID 存储在execute_as_principal_id或sys.sql_modules目录视图中的sys.service_queues列中。
SELF 是队列的默认值。
注意
若要更改目录视图中列execute_as_principal_id的用户 IDsys.service_queues,必须在语句中EXECUTE AS显式指定ALTER QUEUE设置。
OWNER
指定模块内的语句在模块的当前所有者的上下文中执行。 如果模块没有指定所有者,则使用模块架构的所有者。
OWNER 不能为 DDL 或登录触发器指定。
重要
OWNER 必须映射到单一实例帐户,不能是角色或组。
'user_name'
指定模块内的语句在 user_name 指定的用户的上下文中执行。 将根据 user_name 来验证对模块内任意对象的权限。 不能为具有服务器范围或登录触发器的 DDL 触发器指定user_name。 请改用 login_name。
user_name 必须存在于当前数据库中,并且必须是单一实例帐户。
user_name不能是组、角色、证书、密钥或内置帐户,例如NT AUTHORITY\LocalService,NT AUTHORITY\NetworkService或NT AUTHORITY\LocalSystem。
执行上下文的用户 ID 存储在元数据中,可以在或目录视图中的execute_as_principal_idsys.sql_modules列中查看sys.assembly_modules。
“login_name”
指定模块内的语句在 login_name 指定的 SQL Server 登录的上下文中执行。 将根据 login_name 来验证对模块内任意对象的权限。 只能为具有服务器作用域的 DDL 触发器或登录触发器指定 login_name。
login_name不能是组、角色、证书、密钥或内置帐户,例如NT AUTHORITY\LocalService,NT AUTHORITY\NetworkService或NT AUTHORITY\LocalSystem。
注解
数据库引擎对模块所引用对象的权限进行评估的方式取决于调用对象和被引用对象之间存在的所有权链。 在 SQL Server 的早期版本中,所有权链是可以避免授权调用用户访问所有被引用对象权限的唯一方式。
所有权链具有以下限制:
- 仅适用于 DML 语句:
SELECT、INSERT、UPDATE和DELETE。 - 调用和被调用对象的所有者必须相同。
- 不适用于模块中的动态查询。
不论模块中指定的执行上下文如何,以下操作始终适用:
执行模块时,数据库引擎首先验证执行模块的用户是否对模块具有
EXECUTE权限。所有权链规则继续应用。 这意味着如果调用和被调用对象的所有者相同,则不检查对基础对象的权限。
当用户执行已指定为在上下文 CALLER中运行的模块时,将检查用户执行该模块的权限,但对该模块访问的对象执行其他权限检查会针对子句中指定的 EXECUTE AS 用户帐户执行。 实际上,执行模块的用户将扮演指定的用户。
模块子句中指定的 EXECUTE AS 上下文仅在模块执行期间有效。 模块执行完毕后,上下文反转为调用方。
指定用户或登录名
在修改模块以在另一上下文下执行之前,无法删除模块子句中指定的 EXECUTE AS 数据库用户或服务器登录名。
子句中指定的EXECUTE AS用户或登录名必须分别作为主体sys.database_principalssys.server_principals存在,否则创建或更改模块操作将失败。 此外,创建或更改模块的用户必须拥有对主体的 IMPERSONATE 权限。
如果用户通过 Windows 组成员身份隐式访问 SQL Server 的数据库或实例,则当存在以下要求之一时,会在创建模块时隐式创建子句中指定的 EXECUTE AS 用户:
- 指定的用户或登录名是 sysadmin 固定服务器角色的成员。
- 创建模块的用户拥有创建主体的权限。
如果这些要求都不满足,则创建模块的操作将失败。
重要
如果 SQL Server (MSSQLSERVER) 服务作为本地帐户(本地服务或本地用户帐户)运行,则它无权获取子句中指定的 EXECUTE AS Windows 域帐户的组成员身份。 这将导致模块的执行失败。
例如,假设条件如下:
CompanyDomain\SQLUsers组有权访问Sales数据库。CompanyDomain\SqlUser1是数据库的成员SQLUsers,因此有权访问Sales数据库。创建或更改模块的用户拥有创建主体的权限。
当运行以下 CREATE PROCEDURE 语句时,将隐式创建 CompanyDomain\SqlUser1 作为 Sales 数据库中的数据库主体。
USE Sales;
GO
CREATE PROCEDURE dbo.usp_Demo
WITH EXECUTE AS 'CompanyDomain\SqlUser1'
AS
SELECT USER_NAME();
GO
使用 EXECUTE AS CALLER 独立语句
EXECUTE AS CALLER使用模块中的独立语句将执行上下文设置为模块的调用方。
假定 SqlUser2 调用以下存储过程。
CREATE PROCEDURE dbo.usp_Demo
WITH EXECUTE AS 'SqlUser1'
AS
SELECT USER_NAME(); -- Shows execution context is set to SqlUser1.
EXECUTE AS CALLER;
SELECT USER_NAME(); -- Shows execution context is set to SqlUser2, the caller of the module.
REVERT;
SELECT USER_NAME(); -- Shows execution context is set to SqlUser1.
GO
使用 EXECUTE AS 定义自定义权限集
如果要定义自定义权限集,为模块指定执行上下文可能会很有用。 例如,某些操作(例如 TRUNCATE TABLE 没有可授予的权限)。 通过在模块中合并 TRUNCATE TABLE 语句并指定该模块作为有权更改表的用户执行,可以将权限扩展到你向其授予 EXECUTE 模块权限的用户。
若要查看具有指定执行上下文的模块的定义,请使用 sys.sql_modules 目录视图。
最佳做法
指定拥有执行模块中定义的操作所需的最低权限的登录或用户。 例如,除非需要这些权限,否则不要指定数据库所有者帐户。
权限
若要执行指定 EXECUTE AS模块,调用方必须对该模块具有 EXECUTE 权限。
若要执行使用该 EXECUTE AS 模块访问另一个数据库或服务器中的资源的 CLR 模块,目标数据库或服务器必须信任模块的来源数据库(源数据库)的验证器。
若要在创建或修改模块时指定 EXECUTE AS 子句,必须具有 IMPERSONATE 对指定主体以及创建模块的权限。 可以始终扮演自身。 如果未指定或 EXECUTE AS CALLER 指定执行上下文, IMPERSONATE 则不需要权限。
若要指定 通过 Windows 组成员身份隐式访问数据库的login_name 或 user_name ,必须具有 CONTROL 对数据库的权限。
示例
以下示例在 AdventureWorks2022 数据库中创建一个存储过程,并将执行上下文分配给 OWNER。
CREATE PROCEDURE HumanResources.uspEmployeesInDepartment
@DeptValue INT
WITH EXECUTE AS OWNER
AS
SET NOCOUNT ON;
SELECT e.BusinessEntityID,
c.LastName,
c.FirstName,
e.JobTitle
FROM Person.Person AS c
INNER JOIN HumanResources.Employee AS e
ON c.BusinessEntityID = e.BusinessEntityID
INNER JOIN HumanResources.EmployeeDepartmentHistory AS edh
ON e.BusinessEntityID = edh.BusinessEntityID
WHERE edh.DepartmentID = @DeptValue
ORDER BY c.LastName, c.FirstName;
GO
-- Execute the stored procedure by specifying department 5.
EXECUTE HumanResources.uspEmployeesInDepartment 5;
GO