C# 你如何在 .NET 中进行模拟?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/125341/
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
How do you do Impersonation in .NET?
提问by ashwnacharya
Is there a simple out of the box way to impersonate a user in .NET?
是否有一种简单的开箱即用的方式来模拟 .NET 中的用户?
So far I've been using this class from code projectfor all my impersonation requirements.
到目前为止,我一直在使用代码项目中的这个类来满足我的所有模拟需求。
Is there a better way to do it by using .NET Framework?
使用 .NET Framework 有没有更好的方法来做到这一点?
I have a user credential set, (username, password, domain name) which represents the identity I need to impersonate.
我有一个用户凭据集(用户名、密码、域名),它代表我需要模拟的身份。
采纳答案by Eric Schoonover
Here is some good overview of .NET impersonation concepts.
以下是 .NET 模拟概念的一些很好的概述。
- Michiel van Otegem: WindowsImpersonationContext made easy
- WindowsIdentity.Impersonate Method (check out the code samples)
Basically you will be leveraging these classes that are out of the box in the .NET framework:
基本上,您将利用这些在 .NET 框架中开箱即用的类:
The code can often get lengthy though and that is why you see many examples like the one you reference that try to simplify the process.
代码通常会变得冗长,这就是为什么您会看到许多示例,例如您所引用的示例,这些示例试图简化流程。
回答by Esteban Araya
This is probably what you want:
这可能是你想要的:
using System.Security.Principal;
using(WindowsIdentity.GetCurrent().Impersonate())
{
//your code goes here
}
But I really need more details to help you out. You could do impersonation with a config file (if you're trying to do this on a website), or through method decorators (attributes) if it's a WCF service, or through... you get the idea.
但我真的需要更多细节来帮助你。您可以使用配置文件进行模拟(如果您尝试在网站上执行此操作),或者如果它是 WCF 服务,则可以通过方法装饰器(属性)进行模拟,或者通过...您明白了。
Also, if we're talking about impersonating a client that called a particular service (or web app), you need to configure the client correctly so that it passes the appropriate tokens.
此外,如果我们谈论模拟调用特定服务(或 Web 应用程序)的客户端,您需要正确配置客户端,以便它传递适当的令牌。
Finally, if what you really want do is Delegation, you also need to setup AD correctly so that users and machines are trusted for delegation.
最后,如果你真正想做的是委派,你还需要正确设置 AD,以便用户和机器被信任进行委派。
Edit:
Take a look hereto see how to impersonate a different user, and for further documentation.
编辑:
查看此处以了解如何模拟其他用户以及更多文档。
回答by Matt Johnson-Pint
"Impersonation" in the .NET space generally means running code under a specific user account. It is a somewhat separate concept than getting access to that user account via a username and password, although these two ideas pair together frequently. I will describe them both, and then explain how to use my SimpleImpersonationlibrary, which uses them internally.
.NET 空间中的“模拟”通常意味着在特定用户帐户下运行代码。与通过用户名和密码访问该用户帐户相比,它是一个稍微独立的概念,尽管这两个想法经常结合在一起。我将描述它们,然后解释如何使用我的SimpleImpersonation库,它在内部使用它们。
Impersonation
冒充
The APIs for impersonation are provided in .NET via the System.Security.Principal
namespace:
用于模拟的 API 是通过System.Security.Principal
命名空间在 .NET 中提供的:
Newer code (.NET 4.6+, .NET Core, etc.) should generally use
WindowsIdentity.RunImpersonated
, which accepts a handle to the token of the user account, and then either anAction
orFunc<T>
for the code to execute.WindowsIdentity.RunImpersonated(tokenHandle, () => { // do whatever you want as this user. });
or
var result = WindowsIdentity.RunImpersonated(tokenHandle, () => { // do whatever you want as this user. return result; });
Older code used the
WindowsIdentity.Impersonate
method to retrieve aWindowsImpersonationContext
object. This object implementsIDisposable
, so generally should be called from ausing
block.using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(tokenHandle)) { // do whatever you want as this user. }
While this API still exists in .NET Framework, it should generally be avoided, and is not available in .NET Core or .NET Standard.
较新的代码(.NET 4.6+、.NET Core 等)通常应该使用
WindowsIdentity.RunImpersonated
,它接受用户帐户令牌的句柄,然后是要执行的代码的Action
或Func<T>
。WindowsIdentity.RunImpersonated(tokenHandle, () => { // do whatever you want as this user. });
或者
var result = WindowsIdentity.RunImpersonated(tokenHandle, () => { // do whatever you want as this user. return result; });
较旧的代码使用该
WindowsIdentity.Impersonate
方法来检索WindowsImpersonationContext
对象。此对象实现IDisposable
,因此通常应从using
块中调用。using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(tokenHandle)) { // do whatever you want as this user. }
虽然此 API 仍然存在于 .NET Framework 中,但通常应避免使用,并且在 .NET Core 或 .NET Standard 中不可用。
Accessing the User Account
访问用户帐户
The API for using a username and password to gain access to a user account in Windows is LogonUser
- which is a Win32 native API. There is not currently a built-in .NET API for calling it, so one must resort to P/Invoke.
在 Windows 中使用用户名和密码访问用户帐户的LogonUser
API是- 这是一个 Win32 本机 API。目前没有内置的 .NET API 来调用它,所以必须求助于 P/Invoke。
[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);
This is the basic call definition, however there is a lot more to consider to actually using it in production:
这是基本的调用定义,但是在生产中实际使用它还有很多需要考虑的:
- Obtaining a handle with the "safe" access pattern.
- Closing the native handles appropriately
- Code access security (CAS) trust levels (in .NET Framework only)
- Passing
SecureString
when you can collect one safely via user keystrokes.
- 使用“安全”访问模式获取句柄。
- 适当地关闭本机句柄
- 代码访问安全 (CAS) 信任级别(仅在 .NET Framework 中)
- 路过
SecureString
的时候可以通过用户的击键安全地收集一个。
The amount of code to write to illustrate all of this is beyond what should be in a StackOverflow answer, IMHO.
为说明所有这些而编写的代码量超出了 StackOverflow 答案中应有的数量,恕我直言。
A Combined and Easier Approach
一种更简单的组合方法
Instead of writing all of this yourself, consider using my SimpleImpersonationlibrary, which combines impersonation and user access into a single API. It works well in both modern and older code bases, with the same simple API:
与其自己编写所有这些,不如考虑使用我的SimpleImpersonation库,它将模拟和用户访问结合到一个 API 中。它在现代和旧代码库中都能很好地工作,具有相同的简单 API:
var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, logonType, () =>
{
// do whatever you want as this user.
});
or
或者
var credentials = new UserCredentials(domain, username, password);
var result = Impersonation.RunAsUser(credentials, logonType, () =>
{
// do whatever you want as this user.
return something;
});
Note that it is very similar to the WindowsIdentity.RunImpersonated
API, but doesn't require you know anything about token handles.
请注意,它与WindowsIdentity.RunImpersonated
API非常相似,但不需要您了解有关令牌句柄的任何信息。
This is the API as of version 3.0.0. See the project readme for more details. Also note that a previous version of the library used an API with the IDisposable
pattern, similar to WindowsIdentity.Impersonate
. The newer version is much safer, and both are still used internally.
这是 3.0.0 版的 API。有关更多详细信息,请参阅项目自述文件。另请注意,该库的先前版本使用了具有该IDisposable
模式的 API ,类似于WindowsIdentity.Impersonate
. 较新的版本更安全,并且两者仍在内部使用。
回答by toddmo
Here's my vb.net port of Matt Johnson's answer. I added an enum for the logon types. LOGON32_LOGON_INTERACTIVE
was the first enum value that worked for sql server. My connection string was just trusted. No user name / password in the connection string.
这是我的 vb.net 端口 Matt Johnson 的回答。我为登录类型添加了一个枚举。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
You need to Use with a Using
statement to contain some code to run impersonated.
您需要使用 withUsing
语句来包含一些代码以模拟运行。
回答by Cedric Michel
You can use this solution. (Use nuget package) The source code is available on : Github: https://github.com/michelcedric/UserImpersonation
您可以使用此解决方案。(使用nuget包)源代码可在:Github:https: //github.com/michelcedric/UserImpersonation
More detail https://michelcedric.wordpress.com/2015/09/03/usurpation-didentite-dun-user-c-user-impersonation/
更多细节 https://michelcedric.wordpress.com/2015/09/03/usurpation-didentite-dun-user-c-user-impersonation/
回答by Cedric Michel
View more detail from my previous answer I have created an nuget package Nuget
查看我之前的回答中的更多详细信息我创建了一个 nuget 包 Nuget
Code on Github
sample : you can use :
示例:您可以使用:
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");
}
}
Vieuw the full code :
查看完整代码:
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
}
回答by Federico Navarrete
I'm aware that I'm quite late for the party, but I consider that the library from Phillip Allan-Harding, it's the best one for this case and similar ones.
我知道我参加派对已经很晚了,但我认为来自Phillip Allan-Harding的图书馆是本案和类似案件的最佳图书馆。
You only need a small piece of code like this one:
你只需要像这样的一小段代码:
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))
{
}
}
And add his class:
并添加他的课程:
.NET (C#) Impersonation with Network Credentials
My example can be used if you require the impersonated logon to have network credentials, but it has more options.
如果您需要模拟登录具有网络凭据,可以使用我的示例,但它有更多选项。