c# - 是否可以让 SQL 中的 CLR 函数采用特定的 Windows 标识?

标签 c# sql-server impersonation sqlclr windows-identity

我想让一个 CLR 函数在从 SQL Server 代理作业调用时作为特定的 Windows 标识运行。为此,调用该函数的 SQL 语句具有以下形式:

execute as user='domain\username'
select database.schema.function()
revert

为了论证,函数本身可以简单地是这样的:

        [SqlFunction(DataAccess = DataAccessKind.Read)]
        [return: SqlFacet(MaxSize = -1)]
        public static SqlChars GetIdentityInformationImpersonated()
        {
            WindowsIdentity clientId;
            WindowsImpersonationContext impersonatedUser;
            string currentUser;

            clientId = SqlContext.WindowsIdentity;
            impersonatedUser = clientId.Impersonate();

            currentUser = WindowsIdentity.GetCurrent().Name;

            impersonatedUser.Undo();

            return new SqlChars(currentUser);
        }

这会引发异常:

A .NET Framework error occurred during execution of user-defined routine or aggregate "GetIdentityInformationImpersonated": System.NullReferenceException: Object reference not set to an instance of an object. System.NullReferenceException: at WinIdentityDebugging.IdentityResults.GetIdentityInformationImpersonated()

这意味着 SqlContext.WindowsIdentity 为空。现在互联网上有引用资料声称这样的事情是不可能的,例如......

...here...

Keep in mind that SQLCLR impersonation doesn't work with impersonated contexts (EXECUTE AS)

...and here...

attempting to retrieve a SqlContext.WindowsIdentity object is an impersonated security context returns null currently and is doc'd to do so.

...and here...

If the execution is happening in the context of an Execute As Login (statement directly or some module marked with it ) SqlContext.WindowsIdentity will be null unless the login is sysadmin. In this case SqlContext.WindowsIdentity will return the identity under which the server process is running.

但是所有这些引用资料都非常陈旧,我在实际文档中找不到任何此类限制。例如 SqlContext.WindowsIdentity对于 SQL 身份验证调用者,页面仅引用自身为 null:

A WindowsIdentity instance representing the Windows identity of the caller, or null if the client was authenticated using SQL Server Authentication.

所以我希望有一种方法可以实现这一目标。

这样的事情可能吗?还是我运气不好?

最佳答案

您找到的引用资料,即使是旧的,看起来仍然是正确的。我不仅从未见过这种工作的示例,而且我刚刚在 SQL Server 2017 上重新验证了即使通过 EXECUTE AS LOGIN = 'machine_or_domain\account_mame'; 模拟 Windows 登录时,SqlContext.WindowsIdentity 属性仍然返回 null。

如果您需要以特定的 Windows 帐户执行外部命令,您需要在 .NET 代码中授权为该帐户。这需要将程序集设置为 UNSAFE

我记得几年前尝试过,会看看我是否还有那个测试代码。

或者,鉴于您已经在使用 SQL Server Agent,这也许很容易。正如其他人提到的代理一样,您可以简单地将作业步骤类型更改为“操作系统(CmdExec)”并以两种方式之一处理此问题,具体取决于您是否需要 T-SQL 上下文中的输出。

  1. 为您希望函数执行的 Windows 帐户创建凭据
  2. 为该凭据创建代理以用于此类作业步骤(在“SQL Server 代理”->“代理”->“操作系统 (CmdExec)”下的 SSMS 中)
  3. 将 SQL 代理作业步骤更改/创建为“操作系统 (CmdExec)”
  4. 在“运行方式:”下拉列表中,选择您刚刚创建的代理
  5. 取决于您是否需要将 .NET 方法的输出返回到 T-SQL 上下文:
    1. 如果您不需要此方法的任何输出,那么不要在 SQLCLR 中执行此操作,而是创建一个控制台应用程序并将其作为 SQL 代理作业步骤执行。而且,我想即使你确实需要从 T-SQL 向这个函数发送数据,或者对输出做一些事情,在许多/大多数情况下你可以简单地从 .NET 代码连接到 SQL Server 并从控制台应用程序。
    2. 如果您确实需要 T-SQL 上下文中的输出,或者需要从 T-SQL 上下文中传递数据,而这些无法通过在 .NET 代码中执行的查询轻松完成, 然后您可以简单地从 SQL 代理作业步骤执行 SQLCMD.exe,因为它将作为所需的 Windows 帐户连接。从 SQLCMD 中,您执行包含 SQLCLR 函数/存储过程的查询,并且 .NET 模拟将使用该代理帐户(通过凭据),因为外部进程作为该帐户连接到 SQL Server。我已经对此进行了测试,它确实有效。

关于c# - 是否可以让 SQL 中的 CLR 函数采用特定的 Windows 标识?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60430662/

相关文章:

c# - 检查 Unity 构建是否成功(UNITY C#)

c# - 委托(delegate) : Method name expected error

sql - 将多个参数传递给 SQL Server

sql - 如何将映射表中的几行汇总为列?

asp.net - ASP.NET 中的模拟和委托(delegate)(使用 SQL Server)

c# - 控制台窗口一直关闭,即使在我输入其他语句后也是如此

sql - 在 SQL Server 中删除带有联接的表

c# - ASP.NET 托管 Active Directory RSAT powershell 脚本--DC 挂断

c# - 如何检查Windows用户是否可以通过模拟访问(Windows集成安全性身份验证)SQL Server Analysis Services(SSAS)服务器

c# - 将 XmlComment 用于 swagger api 帮助器页面