.net 模拟未将正确的凭据传递给 SQL 服务器

.net impersonate not passing proper credentials to SQL Server

我目前有一个 .net 控制台应用程序需要通过 appConfig 文件接收活动域帐户以连接到数据库。该应用程序正在从 TaskScheduler 执行。我无法使用域帐户执行任务,因为这是一个安全设置(保存密码)。

我在统一设置文件 (appSettings.config) 中有连接字符串,并在控制台应用程序设置文件中设置了身份,包括用户名和密码

我的问题是如何使用任务调度程序来执行作业并在配置文件中包含 username/password?

在测试中,我使用了 "Local Service" 帐户和 "Network Service" 帐户,并收到来自 SQL 服务器的登录错误

Login failed for user 'DOMAIN_NAME\MACHINE_NAME$'. Reason: Could not find a login matching the name provided. [CLIENT: xxx.xxx.xx.xx (ip address of client machine)]

如果我使用具有管理员权限的本地帐户,返回以下错误:

Login failed. The login is from an untrusted domain and cannot be used with Windows authentication. [CLIENT: xxx.xxx.xx.xx]

注释:

所有机器都在同一个域中并且具有连接性

当任务设置为 运行 作为域帐户,并且身份标记没有 username/password 时,任务按设计执行。

appSettings.config

    <?xml version="1.0"?>
    <appSettings>    
        <!-- CONNECTION STRINGS -->
        <add key="connectionString"                 value="Data Source=DB_SERVER_NAME;Initial Catalog=DB_NAME;Integrated Security=SSPI;" />
.....
.....

application.exe.config

   <?xml version="1.0"?>
    <configuration>
      <configSections>

      </configSections>  
     <appSettings file="F:\SPASystems\Conf\appSettings.config" />
      <system.web>  
      <identity impersonate="true" userName="DOMAIN_NAME\svc.ACCOUNT_NAME.user" password="dummy_password"/>
        <membership defaultProvider="ClientAuthenticationMembershipProvider">
....
....

如果进程在域帐户下运行时有效,但在模拟域帐户时无效,我很确定这是因为模拟不会在机器之间流动。来自 MSDN:

Impersonation flows the original caller's identity to back-end resources on the same computer. Delegation flows the original caller's identity to back-end resources on computers other than the computer running the service.

您的 SQL 服务器可能在不同的机器上,因此虽然您是在模拟,但当进程开始与另一台机器通信时,它会开始使用该进程启动时使用的原始帐户。

因此,您需要使用委托

您可能还会发现 this thread 有帮助。

我的最终解决方案是使用模拟并通过委托方法在模拟上下文中执行。 这些是我用来完成此操作的代码片段:

internal class NativeMethods
    {
        // closes open handes returned by LogonUser
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        // obtains user token
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    }

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public sealed class Impersonation
    {
        /// <summary>
        /// impersonates a user based on username/password provided. executed method after impersonation and then reverts impersonation when task/method is complete.
        /// </summary>
        /// <param name="userName">username to impersonate</param>
        /// <param name="password">password for user account</param>
        /// <param name="domain">domain of user to impersonate</param>
        /// <param name="action">method to invoke after impersonation</param>
        /// <param name="logonType">LogonType to use, defaulted to Network</param>
        /// <param name="logonProvider">LogonProvider type, defaulted to default</param>
        public static void impersonate(string userName, string password, string domain, Action action, int logonType = 2, int logonProvider = 0)
        {
            //elevate privileges before doing file copy to handle domain security
            WindowsImpersonationContext context = null;
            IntPtr userHandle = IntPtr.Zero;
            try
            {
                Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);
                // Call LogonUser to get a token for the user
                bool loggedOn = NativeMethods.LogonUser(userName,
                                            domain,
                                            password,
                                            logonType,
                                            logonProvider,
                                            ref userHandle);
                if (!loggedOn)
                {
                    Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
                }

                // Begin impersonating the user
                context = WindowsIdentity.Impersonate(userHandle);

                Console.WriteLine("windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);                
                //execute actions under impersonated user
                action();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception impersonating user: " + ex.Message);
            }
            finally
            {
                // Clean up
                if (context != null)
                {
                    context.Undo();
                }

                if (userHandle != IntPtr.Zero)
                {
                    NativeMethods.CloseHandle(userHandle);
                }
            }
        }

然后我可以通过以下方式调用和使用模拟上下文:

Impersonation.impersonate(impersonationUserName, impersonationPassword, impersonationDomain, () => processReport(args));

然后使用域、用户名和密码中提供的帐户信息在上下文中执行方法 processReport(string[] args)。如果需要,这些也可以是安全字符串。