你如何在.NET中进行模拟?

时间:2008-09-24 03:55:09

标签: c# .net impersonation

是否有一种简单易用的方式来模拟.NET中的用户?

到目前为止,我一直在使用this class from code project来满足我的所有模拟要求。

使用.NET Framework有更好的方法吗?

我有一个用户凭据集,(用户名,密码,域名)代表我冒充身份所需的身份。

7 个答案:

答案 0 :(得分:265)

.NET空间中的“模拟”通常意味着在特定用户帐户下运行代码。这是一个有点单独的概念,而不是通过用户名和密码访问该用户帐户,尽管这两个想法经常配对。我将对它们进行描述,然后解释如何使用我的SimpleImpersonation库,它在内部使用它们。

模拟

用于模拟的API在.NET中通过System.Security.Principal命名空间提供:

  • 较新的代码(.NET 4.6 +,.NET Core等)通常应使用WindowsIdentity.RunImpersonated,它接受​​用户帐户令牌的句柄,然后使用{{1} }或Action代码执行。

    Func<T>

    WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
    });
    
  • 较旧的代码使用WindowsIdentity.Impersonate方法检索var result = WindowsIdentity.RunImpersonated(tokenHandle, () => { // do whatever you want as this user. return result; }); 对象。此对象实现WindowsImpersonationContext,因此通常应从IDisposable块调用。

    using

    虽然此API仍然存在于.NET Framework中,但通常应该避免使用它,并且在.NET Core或.NET Standard中不可用。

访问用户帐户

使用用户名和密码访问Windows中的用户帐户的API是LogonUser - 这是一个Win32本机API。目前没有用于调用它的内置.NET API,因此必须使用P / Invoke。

using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(tokenHandle))
{
    // do whatever you want as this user.
}

这是基本的呼叫定义,但在生产中实际使用它还有很多需要考虑的事项:

  • 获取具有“安全”访问模式的句柄。
  • 正确关闭原生句柄
  • 代码访问安全性(CAS)信任级别(仅限.NET Framework)
  • 当您可以通过用户按键安全地收集一个时,传递[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);

要编写的代码量说明所有这些超出了StackOverflow答案中的应用程序,恕我直言。

一种综合且更简单的方法

不要自己编写所有这些,而是​​考虑使用我的SimpleImpersonation库,它将模拟和用户访问结合到一个API中。它适用于现代和旧代码库,使用相同的简单API:

SecureString

var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, logonType, () =>
{
    // do whatever you want as this user.
}); 

请注意,它与var credentials = new UserCredentials(domain, username, password); var result = Impersonation.RunAsUser(credentials, logonType, () => { // do whatever you want as this user. return something; }); API非常相似,但不要求您了解有关令牌句柄的任何信息。

这是从3.0.0版开始的API。有关详细信息,请参阅项目自述文件。另请注意,该库的先前版本使用了具有WindowsIdentity.RunImpersonated模式的API,类似于IDisposable。较新版本更安全,两者仍在内部使用。

答案 1 :(得分:57)

以下是.NET模拟概念的一些很好的概述。

基本上,您将利用.NET框架中开箱即用的这些类:

代码通常会变得冗长,这就是为什么你会看到许多例子,比如你引用的那些试图简化过程的例子。

答案 2 :(得分:19)

这可能是你想要的:

using System.Security.Principal;
using(WindowsIdentity.GetCurrent().Impersonate())
{
     //your code goes here
}

但我真的需要更多细节来帮助你。您可以使用配置文件进行模拟(如果您尝试在网站上执行此操作),或者通过方法装饰器(属性)进行模拟,如果它是WCF服务,或者通过......您可以理解。

另外,如果我们谈论模仿调用特定服务(或Web应用程序)的客户端,则需要正确配置客户端,以便它传递适当的令牌。

最后,如果您真正想做的是委托,您还需要正确设置AD,以便信任用户和计算机进行委派。

修改
看看here,了解如何冒充不同的用户,以及进一步的文档。

答案 3 :(得分:6)

这是我对马特约翰逊的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语句来包含一些代码来运行模拟。

答案 4 :(得分:3)

从我之前的回答中查看更多细节 我创建了一个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");
               }
           }

Vieuw完整代码:

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
}

答案 5 :(得分:2)

我知道我参加派对的时间已经很晚了,但是我认为来自Phillip Allan-Harding的图书馆,对于这个案例和类似的图书馆来说是最好的。

你只需要一小段像这样的代码:

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))
    {
    }
}

添加他的课程:

.NET (C#) Impersonation with Network Credentials

如果您需要模拟登录以获得网络凭据,则可以使用我的示例,但它有更多选项。

答案 6 :(得分:0)

您可以使用此解决方案。 (使用nuget包) 源代码可在以下网站获得:Github: https://github.com/michelcedric/UserImpersonation

更多细节 https://michelcedric.wordpress.com/2015/09/03/usurpation-didentite-dun-user-c-user-impersonation/