C# 模拟 Windows 用户
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9909784/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Impersonating a Windows user
提问by Kyle Johnson
I am using the code to impersonate a user account to get access to a file share.
我正在使用该代码模拟用户帐户以访问文件共享。
public class Impersonator :
IDisposable
{
#region Public methods.
// ------------------------------------------------------------------
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// Please note that the account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public Impersonator(
string userName,
string domainName,
string password )
{
ImpersonateValidUser( userName, domainName, password );
}
// ------------------------------------------------------------------
#endregion
#region IDisposable member.
// ------------------------------------------------------------------
public void Dispose()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region P/Invoke.
// ------------------------------------------------------------------
[DllImport("advapi32.dll", SetLastError=true)]
private static extern int LogonUser(
string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern int DuplicateToken(
IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool CloseHandle(
IntPtr handle);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
// ------------------------------------------------------------------
#endregion
#region Private member.
// ------------------------------------------------------------------
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
private void ImpersonateValidUser(
string userName,
string domain,
string password )
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser(
userName,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref token ) != 0 )
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
impersonationContext = tempWindowsIdentity.Impersonate();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token!= IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate!=IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void UndoImpersonation()
{
if ( impersonationContext!=null )
{
impersonationContext.Undo();
}
}
private WindowsImpersonationContext impersonationContext = null;
// ------------------------------------------------------------------
#endregion
}
Then using:
然后使用:
using (new Impersonator("username", "domain", "password"))
{
Process.Start("explorer.exe", @"/root,\server01-Prod\abc");
}
I get an "Access Denied" error.
我收到“拒绝访问”错误。
This user supposely has access to this share. I can map a drive, use "net use" but this code will not work. Now I am thinking it is the code. Does anyone see anything? Is there a better way of doing this?
该用户应该有权访问此共享。我可以映射驱动器,使用“net use”,但此代码不起作用。现在我想这是代码。有没有人看到什么?有没有更好的方法来做到这一点?
采纳答案by Royi Namir
try this :
尝试这个 :
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
Usage :
用法 :
IntPtr userToken = IntPtr.Zero;
bool success = External.LogonUser(
"john.doe",
"domain.com",
"MyPassword",
(int) AdvApi32Utility.LogonType.LOGON32_LOGON_INTERACTIVE, //2
(int) AdvApi32Utility.LogonProvider.LOGON32_PROVIDER_DEFAULT, //0
out userToken);
if (!success)
{
throw new SecurityException("Logon user failed");
}
using (WindowsIdentity.Impersonate(userToken))
{
Process.Start("explorer.exe", @"/root,\server01-Prod\abc");
}
回答by David Hoerster
Instead of using your Impersonatorclass, what happens when you call Process.Startand pass in a ProcessStartInfoinstance that contains the username, password and domain that you want to run the process as?
如果不使用您的Impersonator类,当您调用Process.Start并传入一个ProcessStartInfo包含您要运行该进程的用户名、密码和域的实例时会发生什么?
Perhaps, if that works, then your Impersonatorclass should create a ProcessStartInfoinstance and use that to create new processes (encapsulate that within the class itself).
也许,如果可行,那么您的Impersonator类应该创建一个ProcessStartInfo实例并使用它来创建新进程(将其封装在类本身中)。
var psi = new ProcessStartInfo("explorer.exe", @"/root,\server01-Prod\abc");
psi.Domain = domain;
psi.UserName = username;
psi.Password = password;
psi.WorkingDirectory = workingDir;
Process.Start(psi);
Also, per the MSDN docs...
此外,根据MSDN 文档...
Setting the Domain, UserName, and the Password properties in a ProcessStartInfo object is the recommended practice for starting a process with user credentials.
在 ProcessStartInfo 对象中设置 Domain、UserName 和 Password 属性是使用用户凭据启动进程的推荐做法。
You should also set the working directory when starting a process with different user creds.
在使用不同的用户凭据启动进程时,您还应该设置工作目录。
回答by Peter
If I'm understanding correctly, your intention is to run the process in the impersonation context.
如果我理解正确,您的意图是在模拟上下文中运行该过程。
The doc from CreateProcess (which is used by Process.Start) says: If the calling process is impersonating another user, the new process uses the token for the calling process, not the impersonation token. To run the new process in the security context of the user represented by the impersonation token, use the CreateProcessAsUser or CreateProcessWithLogonW function.
CreateProcess 的文档(由 Process.Start 使用)说:如果调用进程正在模拟另一个用户,则新进程使用调用进程的令牌,而不是模拟令牌。要在模拟令牌表示的用户的安全上下文中运行新进程,请使用 CreateProcessAsUser 或 CreateProcessWithLogonW 函数。
So, you're using the wrong API for doing that.
因此,您使用了错误的 API 来执行此操作。

