有没有一种简单的、开箱即用的方法在 .NET 中模拟用户身份?
到目前为止,我一直在使用来自代码项目的这个类来满足我的所有模拟要求。
有没有更好的方法可以利用 .NET Framework 来实现它?
我有一个用户凭据集合(用户名、密码、域名),代表着我需要模拟的身份。
有没有一种简单的、开箱即用的方法在 .NET 中模拟用户身份?
到目前为止,我一直在使用来自代码项目的这个类来满足我的所有模拟要求。
有没有更好的方法可以利用 .NET Framework 来实现它?
我有一个用户凭据集合(用户名、密码、域名),代表着我需要模拟的身份。
.NET中的"模拟用户身份"通常是指在特定用户帐户下运行代码。这是一个比通过用户名和密码获取该用户帐户访问权限要稍微独立一些的概念,但这两个想法经常一起使用。
模拟用户身份的API通过System.Security.Principal
命名空间在.NET中提供:
较新的代码通常应使用WindowsIdentity.RunImpersonated
,它接受用户帐户令牌的句柄,然后执行一个Action
或Func<T>
来执行代码。
WindowsIdentity.RunImpersonated(userHandle, () =>
{
// do whatever you want as this user.
});
或者var result = WindowsIdentity.RunImpersonated(userHandle, () =>
{
// do whatever you want as this user.
return result;
});
此外,.NET 5+提供了异步任务的 WindowsIdentity.RunImpersonatedAsync
方法。如果您使用旧版本,可以通过引入System.Security.Principal.Windows Nuget包来使用该方法。
await WindowsIdentity.RunImpersonatedAsync(userHandle, async () =>
{
// do whatever you want as this user.
});
或者var result = await WindowsIdentity.RunImpersonated(userHandle, async () =>
{
// do whatever you want as this user.
return result;
});
旧代码使用WindowsIdentity.Impersonate
方法来检索一个 WindowsImpersonationContext
对象。该对象实现了 IDisposable
,所以通常应该在 using
块中调用。
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(userHandle))
{
// do whatever you want as this user.
}
虽然这个API在.NET Framework中仍然存在,但通常应该避免使用。
使用用户名和密码访问Windows用户帐户的API是Win32本地APILogonUser
。目前没有内置的托管.NET API可以调用它。
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
这是基本的调用定义,但实际使用它在生产中需要考虑更多:
SecureString
。不要自己编写该代码,考虑使用我的SimpleImpersonation库,它提供了一个托管包装器来获取用户句柄的LogonUser
API:
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
using SimpleImpersonation;
var credentials = new UserCredentials(domain, username, password);
using SafeAccessTokenHandle userHandle = credentials.LogonUser(LogonType.Interactive); // or another LogonType
现在,您可以使用上面第一节中提到的任何方法来使用userHandle
。这是自SimpleImpersonation库4.0.0版本以来的首选API。有关更多详细信息,请参阅项目自述文件。
重要的是要认识到模拟是一个本地计算机概念。无法使用仅已知于远程计算机的用户进行模拟。如果您想要访问远程计算机上的资源,则本地计算机和远程计算机必须连接到同一个域,或者两台计算机的域之间需要建立信任关系。如果任一计算机没有域,则无法使用LogonUser
或SimpleImpersonation连接到该计算机。
以下是 .NET 身份模拟相关概念的概述:
你将会利用 .NET 框架中提供的以下类:
代码通常会变得很冗长,这就是为什么你会看到许多尝试简化该过程的示例代码。
这很可能是你想要的:
using System.Security.Principal;
using(WindowsIdentity.GetCurrent().Impersonate())
{
//your code goes here
}
但我需要更多细节来帮助你解决问题。如果你正在尝试在网站上进行模拟,则可以使用配置文件进行模拟,如果是WCF服务,则可以通过方法修饰符(属性)进行模拟,或者......你懂的。
此外,如果我们要讨论模拟调用特定服务(或Web应用程序)的客户端,则需要正确配置客户端,以便它传递适当的令牌。
最后,如果您真正想要做的是委派,则还需要正确设置AD,以使用户和计算机受信任而可供委派。
编辑:
在这里查看如何模拟不同的用户,并获取进一步的文档。
以下是我对Matt Johnson答案的VB.Net版本,我添加了一个登陆类型的枚举。其中LOGON32_LOGON_INTERACTIVE
是第一个适用于SQL Server的枚举值。我的连接字符串是可信任的,没有在连接字符串中包含用户名/密码。
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
Public Class Impersonation
Implements IDisposable
Public Enum LogonTypes
''' <summary>
''' This logon type is intended for users who will be interactively using the computer, such as a user being logged on
''' by a terminal server, remote shell, or similar process.
''' This logon type has the additional expense of caching logon information for disconnected operations;
''' therefore, it is inappropriate for some client/server applications,
''' such as a mail server.
''' </summary>
LOGON32_LOGON_INTERACTIVE = 2
''' <summary>
''' This logon type is intended for high performance servers to authenticate plaintext passwords.
''' The LogonUser function does not cache credentials for this logon type.
''' </summary>
LOGON32_LOGON_NETWORK = 3
''' <summary>
''' This logon type is intended for batch servers, where processes may be executing on behalf of a user without
''' their direct intervention. This type is also for higher performance servers that process many plaintext
''' authentication attempts at a time, such as mail or Web servers.
''' The LogonUser function does not cache credentials for this logon type.
''' </summary>
LOGON32_LOGON_BATCH = 4
''' <summary>
''' Indicates a service-type logon. The account provided must have the service privilege enabled.
''' </summary>
LOGON32_LOGON_SERVICE = 5
''' <summary>
''' This logon type is for GINA DLLs that log on users who will be interactively using the computer.
''' This logon type can generate a unique audit record that shows when the workstation was unlocked.
''' </summary>
LOGON32_LOGON_UNLOCK = 7
''' <summary>
''' This logon type preserves the name and password in the authentication package, which allows the server to make
''' connections to other network servers while impersonating the client. A server can accept plaintext credentials
''' from a client, call LogonUser, verify that the user can access the system across the network, and still
''' communicate with other servers.
''' NOTE: Windows NT: This value is not supported.
''' </summary>
LOGON32_LOGON_NETWORK_CLEARTEXT = 8
''' <summary>
''' This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
''' The new logon session has the same local identifier but uses different credentials for other network connections.
''' NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
''' NOTE: Windows NT: This value is not supported.
''' </summary>
LOGON32_LOGON_NEW_CREDENTIALS = 9
End Enum
<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function LogonUser(lpszUsername As [String], lpszDomain As [String], lpszPassword As [String], dwLogonType As Integer, dwLogonProvider As Integer, ByRef phToken As SafeTokenHandle) As Boolean
End Function
Public Sub New(Domain As String, UserName As String, Password As String, Optional LogonType As LogonTypes = LogonTypes.LOGON32_LOGON_INTERACTIVE)
Dim ok = LogonUser(UserName, Domain, Password, LogonType, 0, _SafeTokenHandle)
If Not ok Then
Dim errorCode = Marshal.GetLastWin32Error()
Throw New ApplicationException(String.Format("Could not impersonate the elevated user. LogonUser returned error code {0}.", errorCode))
End If
WindowsImpersonationContext = WindowsIdentity.Impersonate(_SafeTokenHandle.DangerousGetHandle())
End Sub
Private ReadOnly _SafeTokenHandle As New SafeTokenHandle
Private ReadOnly WindowsImpersonationContext As WindowsImpersonationContext
Public Sub Dispose() Implements System.IDisposable.Dispose
Me.WindowsImpersonationContext.Dispose()
Me._SafeTokenHandle.Dispose()
End Sub
Public NotInheritable Class SafeTokenHandle
Inherits SafeHandleZeroOrMinusOneIsInvalid
<DllImport("kernel32.dll")> _
<ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)> _
<SuppressUnmanagedCodeSecurity()> _
Private Shared Function CloseHandle(handle As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Public Sub New()
MyBase.New(True)
End Sub
Protected Overrides Function ReleaseHandle() As Boolean
Return CloseHandle(handle)
End Function
End Class
End Class
Using
语句来容纳需要以模拟用户身份运行的一些代码。请查看我之前的回答以获取更多详细信息。
我已经创建了一个Nuget包(Nuget)
在Github上的代码
示例:您可以使用:
string login = "";
string domain = "";
string password = "";
using (UserImpersonation user = new UserImpersonation(login, domain, password))
{
if (user.ImpersonateValidUser())
{
File.WriteAllText("test.txt", "your text");
Console.WriteLine("File writed");
}
else
{
Console.WriteLine("User not connected");
}
}
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
/// <summary>
/// Object to change the user authticated
/// </summary>
public class UserImpersonation : IDisposable
{
/// <summary>
/// Logon method (check athetification) from advapi32.dll
/// </summary>
/// <param name="lpszUserName"></param>
/// <param name="lpszDomain"></param>
/// <param name="lpszPassword"></param>
/// <param name="dwLogonType"></param>
/// <param name="dwLogonProvider"></param>
/// <param name="phToken"></param>
/// <returns></returns>
[DllImport("advapi32.dll")]
private static extern bool LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
/// <summary>
/// Close
/// </summary>
/// <param name="handle"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
private WindowsImpersonationContext _windowsImpersonationContext;
private IntPtr _tokenHandle;
private string _userName;
private string _domain;
private string _passWord;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
/// <summary>
/// Initialize a UserImpersonation
/// </summary>
/// <param name="userName"></param>
/// <param name="domain"></param>
/// <param name="passWord"></param>
public UserImpersonation(string userName, string domain, string passWord)
{
_userName = userName;
_domain = domain;
_passWord = passWord;
}
/// <summary>
/// Valiate the user inforamtion
/// </summary>
/// <returns></returns>
public bool ImpersonateValidUser()
{
bool returnValue = LogonUser(_userName, _domain, _passWord,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref _tokenHandle);
if (false == returnValue)
{
return false;
}
WindowsIdentity newId = new WindowsIdentity(_tokenHandle);
_windowsImpersonationContext = newId.Impersonate();
return true;
}
#region IDisposable Members
/// <summary>
/// Dispose the UserImpersonation connection
/// </summary>
public void Dispose()
{
if (_windowsImpersonationContext != null)
_windowsImpersonationContext.Undo();
if (_tokenHandle != IntPtr.Zero)
CloseHandle(_tokenHandle);
}
#endregion
}
private const string LOGIN = "mamy";
private const string DOMAIN = "mongo";
private const string PASSWORD = "HelloMongo2017";
private void DBConnection()
{
using (Impersonator user = new Impersonator(LOGIN, DOMAIN, PASSWORD, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
}
}
并添加他的类:
如果您需要模拟登录具有网络凭据,可以使用我的示例,但它具有更多选项。
您可以使用此解决方案(使用NuGet包)。 源代码可在Github上找到: https://github.com/michelcedric/UserImpersonation
更多细节请参见 https://michelcedric.wordpress.com/2015/09/03/usurpation-didentite-dun-user-c-user-impersonation/