了解上下文切换

执行上下文由连接到相应会话的用户或登录名确定,或由执行(调用)相应模块的用户或登录名确定。它建立了检查执行语句或执行操作的权限时所依据的标识。在 SQL Server 中,可以通过执行 EXECUTE AS 语句或在模块中指定 EXECUTE AS 子句将执行上下文切换到其他用户或登录。在切换上下文后,SQL Server 将检查该帐户的登录或用户权限,而不是检查调用 EXECUTE AS 语句或模块的人员的权限。在会话或模块执行的其余部分,或在显式恢复上下文切换之前,模拟数据库用户或 SQL Server 登录名。有关执行上下文的详细信息,请参阅了解执行上下文

显式上下文切换

可以通过在 EXECUTE AS 语句中指定用户或登录名来显式更改会话或模块的执行上下文。直到发生下列事件之一时,模拟才会失效:

  • 删除会话。

  • 上下文切换到另一个登录名或用户。

  • 上下文恢复到以前的执行上下文。

使用 EXECUTE AS 显式模拟其他用户与 SQL Server 早期版本中的 SETUSER 类似。有关详细信息,请参阅 EXECUTE AS 与 SETUSER

显式服务器级上下文切换

若要在服务器级切换执行上下文,请使用 EXECUTE AS LOGIN = 'login_name' 语句。登录名必须在 sys.server_principals 中作为主体存在,并且语句调用方必须具有指定登录名的 IMPERSONATE 权限。

下面是执行上下文处于服务器级别时模拟的作用域:

  • login_name 的登录令牌由 SQL Server 实例进行身份验证,并在该实例内有效。

  • login_name 的服务器级权限和角色成员身份得到遵守。

使用 REVERT 语句可以返回到以前的上下文。REVERT 语句的调用方必须位于发生模拟的同一数据库中。

示例

在下面的示例中,Adventure Works Cycles 的网络管理员 Peter Connelly 要为新雇员 Jinghao Liuhas 创建一个 SQL Server 登录帐户。Peter 的 SQL Server 登录名没有创建 SQL Server 登录名所需的服务器级权限,但是它具有 adventure-works\dan1(一个具有所需服务器级权限的 SQL Server 登录名)的 IMPERSONATE 权限。Peter 连接到 SQL Server 后,便会从他的 SQL Server 登录帐户派生会话的执行上下文。为了创建 SQL Server 登录帐户,Peter 暂时采用 adventure-works\dan1 的执行上下文。然后他创建登录帐户。最后,他放弃所采用的权限。

-- Switch execution context to the adventure-works\dan1 login account.
EXECUTE AS LOGIN = 'adventure-works\dan1';
-- Create the new login account.
CREATE LOGIN Jinghao1 WITH PASSWORD = '3KHJ6dhx(0xVYsdf';
-- Revert to the previous execution context.
REVERT;

显式数据库级上下文切换

若要在数据库级切换上下文,请使用 EXECUTE AS USER = 'user_name' 语句。用户名必须在 sys.database_principals 中作为主体存在,并且语句调用方必须具有指定用户名的 IMPERSONATE 权限。

下面是执行上下文处于数据库级别时模拟的作用域:

  • user_name 的用户令牌由 SQL Server 实例进行身份验证,并在当前数据库中有效。有关如何在当前数据库作用域之外展开用户模拟的信息,请参阅使用 EXECUTE AS 扩展数据库模拟

  • 当前数据库的 user_name 的数据库级权限和角色成员身份得到遵守。对用户令牌或角色成员身份中的标识显式授予的服务器级权限不被遵守。

使用 REVERT 语句可以返回到以前的上下文。REVERT 语句的调用方必须位于发生模拟的同一数据库中。

示例

在下例中,Adventure Works Cycles 的数据库管理员 François Ajenstat 要对 AdventureWorksDW 数据库运行 DBCC CHECKDB 语句,但他没有执行此操作所需的数据库级权限。然而,他具有用户 dan1(一个具有所需权限的帐户)的 IMPERSONATE 权限。

François 连接到 AdventureWorksDW 数据库后,执行上下文将映射到其用户安全令牌。根据他的用户令牌中的主要主体和辅助主体检查执行语句的权限。由于他没有运行 DBCC CHECKDB 语句所需的权限,因此他执行下列语句。

-- EXECUTE AS USER = 'dan1';
-- Create a table in dan1's default schema
CREATE TABLE t_NewTable( data nvarchar(100) );
go
-- Revert to the previous execution context.
REVERT
go;

隐式上下文切换

可以通过在模块定义的 EXECUTE AS 子句中指定用户或登录名来隐式更改模块(例如存储过程、触发器、队列或用户定义函数)的执行上下文。

通过指定执行模块时所用的上下文,可以控制 SQL Server 使用哪个用户帐户来验证模块所引用的任何对象的权限。这有助于人们更灵活、有力地管理用户定义的模块及其所引用对象所形成的对象链中的权限。可以授予用户对模块自身的权限,而无需授予用户对被引用对象的显式权限。只有模块模拟的用户需要具有模块所访问的对象的权限。

模拟的级别由定义模拟所在的模块的类型确定。

可以在以下模块中定义服务器级模拟:

  • DDL 触发器

服务器级模拟的作用域与以前在“显式服务器级上下文切换”中定义的作用域相同。

可以在下列模块中定义数据库级模拟:

  • DML 触发器

  • 队列

  • 存储过程

  • 用户定义函数

  • 数据库级模拟的作用域与以前在“显式数据库级上下文切换”中定义的作用域相同。

  • 有关隐式上下文切换的详细信息,请参阅在模块中使用 EXECUTE AS

示例

在下面的示例中,Mary 是表 MyTable 的所有者。她想使用户 Scott 能够截断该表,但 Scott 不具有该表的直接权限。因此,她创建存储过程 dbo.usp_TruncateMyTable,并将该过程的 EXECUTE 权限授予 Scott。当 Scott 执行存储过程时,数据库引擎将视同是 Mary 亲自执行存储过程来验证其截断该表的权限。由于 Mary 是该表的所有者,即使 Scott 不具有该表自身的直接权限,该语句也会成功。

CREATE PROCEDURE dbo.usp_TruncateMyTable
WITH EXECUTE AS SELF
AS TRUNCATE TABLE MyDB..MyTable;