.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)
。如果需要,这些也可以是安全字符串。
我目前有一个 .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)
。如果需要,这些也可以是安全字符串。