【问题标题】:How to Impersonate a user in managed code?如何在托管代码中模拟用户?
【发布时间】:2011-02-18 00:45:41
【问题描述】:

给定用户名和密码,我如何模拟该用户并以该用户身份运行一些代码。

托管我的意思是没有 pinvokes 或 dllimports

【问题讨论】:

    标签: .net impersonation


    【解决方案1】:

    有一个类似的问题,答案很好here

    查看WindowsImpersonationContext 了解更多信息(那里还有另一个很棒的代码示例)

    【讨论】:

    • 如果它是一个 ASP.NET 网站,那么可以通过在 web.config 中设置 <impersonation /> 元素来完成。否则你将不得不使用进口 :(
    • @Simon:模拟在安装过程中或“以”其他人(例如管理员)身份运行应用程序时也很有帮助。
    【解决方案2】:

    参见ImpersonationHelper 中的Is it possible to safely get a SecureString value from VB .NET?。该代码已准备好用于生产且健壮。

    它支持IDisposable,包含RunAs 方法(非常宝贵),密码被处理为SecureString,以及其他一些有用的东西。我还提供了ImpersonationHelper 类的测试代码,它们对故障排除非常有帮助,它们是派上用场的SecureString 扩展方法。

    【讨论】:

      【解决方案3】:

      我把它归结为两种简单的方法:

      public bool ImpersonateValidUser(String userName, String domain, String password)
      public void UndoImpersonation()
      

      您可以直接复制/粘贴下面的类并在您的项目中使用它:

          class ImpersonationContext
          {
              [DllImport("advapi32.dll")]
              public static extern int LogonUserA(String lpszUserName,
                  String lpszDomain,
                  String lpszPassword,
                  int dwLogonType,
                  int dwLogonProvider,
                  ref IntPtr phToken);
              [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
              public static extern int DuplicateToken(IntPtr hToken,
                  int impersonationLevel,
                  ref IntPtr hNewToken);
      
              [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
              public static extern bool RevertToSelf();
      
              [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
              public static extern bool CloseHandle(IntPtr handle);
      
              public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
              public const int LOGON32_LOGON_INTERACTIVE = 2;
              public const int LOGON32_PROVIDER_DEFAULT = 0;
              public const int LOGON32_PROVIDER_WINNT50 = 3;
              WindowsImpersonationContext impersonationContext;
      
              public bool ImpersonateValidUser(String userName, String domain, String password)
              {
                  WindowsIdentity tempWindowsIdentity;
                  IntPtr token = IntPtr.Zero;
                  IntPtr tokenDuplicate = IntPtr.Zero;
      
                  if (RevertToSelf())
                  {
                      if (LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
                          LOGON32_PROVIDER_WINNT50, ref token) != 0)
                      {
                          if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                          {
                              tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                              impersonationContext = tempWindowsIdentity.Impersonate();
                              if (impersonationContext != null)
                              {
                                  CloseHandle(token);
                                  CloseHandle(tokenDuplicate);
                                  return true;
                              }
                          }
                      }
                  }
                  if (token != IntPtr.Zero)
                      CloseHandle(token);
                  if (tokenDuplicate != IntPtr.Zero)
                      CloseHandle(tokenDuplicate);
                  return false;
              }
      
              public void UndoImpersonation()
              {
                  impersonationContext.Undo();
              }
          }
      

      【讨论】:

      • 当然,归结为两种方法。但是,您的代码没有错误处理,如果确实发生错误,则不会释放它创建的句柄。
      【解决方案4】:

      这是我们创建的包装类,可以在多个不同的 Windows 平台上运行:

      public class Impersonator
      {
          // constants from winbase.h
          public const int LOGON32_LOGON_INTERACTIVE = 2;
          public const int LOGON32_LOGON_NETWORK = 3;
          public const int LOGON32_LOGON_BATCH = 4;
          public const int LOGON32_LOGON_SERVICE = 5;
          public const int LOGON32_LOGON_UNLOCK = 7;
          public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
          public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
      
          public const int LOGON32_PROVIDER_DEFAULT = 0;
          public const int LOGON32_PROVIDER_WINNT35 = 1;
          public const int LOGON32_PROVIDER_WINNT40 = 2;
          public const int LOGON32_PROVIDER_WINNT50 = 3;
      
          [DllImport("advapi32.dll", SetLastError=true)]
          public static extern int LogonUserA(String lpszUserName, 
              String lpszDomain,
              String lpszPassword,
              int dwLogonType, 
              int dwLogonProvider,
              ref IntPtr phToken);
          [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
          public static extern int DuplicateToken(IntPtr hToken, 
              int impersonationLevel,  
              ref IntPtr hNewToken);
      
          [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
          public static extern bool RevertToSelf();
      
          [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
          public static extern  bool CloseHandle(IntPtr handle);
      
          public static WindowsImpersonationContext LogOn(string userName, string password)
          {
              return LogOn(userName, password, "");
          }
      
          public static WindowsImpersonationContext LogOn(string userName, string password, string domain)
          {
              WindowsIdentity tempWindowsIdentity;
              WindowsImpersonationContext impersonationContext;
              IntPtr token = IntPtr.Zero;
              IntPtr tokenDuplicate = IntPtr.Zero;
      
              if(RevertToSelf())
              {
                  if (LogonUserA(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
                      LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                  {
                      if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                      {
                          tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                          impersonationContext = tempWindowsIdentity.Impersonate();
                          if (impersonationContext != null)
                          {
                              CloseHandle(token);
                              CloseHandle(tokenDuplicate);
                              return impersonationContext;
                          }
                      }
                  }
                  else
                  {
                      var win32 = new Win32Exception(Marshal.GetLastWin32Error());
                      //throw new Exception(string.Format("{0}, Domain:{1}, User:{2}, Password:{3}",
                      //    win32.Message, domain, userName, password));
                      throw new Exception(win32.Message);
                  }
              }
              if(token!= IntPtr.Zero)
                  CloseHandle(token);
              if(tokenDuplicate!=IntPtr.Zero)
                  CloseHandle(tokenDuplicate);
              return null; // Failed to impersonate
          }
      
          public static bool LogOff(WindowsImpersonationContext context)
          {
              bool result = false;
              try
              {
                  if (context != null)
                  {
                      context.Undo();
                      result = true;
                  }
              }
              catch
              {
                  result = false;
              }
              return result;
          }
      }
      

      【讨论】:

      • 您在哪里找到 LOGON32_PROVIDER_x 值?
      • @MicahBurnett 我看了这段代码已经有一段时间了,但评论说我是从 winbase.h 得到的。 MSDN 搜索或 winbase.h 搜索应该会给你一些参考。还有这个msdn.microsoft.com/en-us/library/windows/desktop/…
      【解决方案5】:

      我看到的所有示例都没有考虑到登录类型不是一刀切的解决方案这一事实。

      例如,这仅在您模拟的用户有权登录目标系统时才有效。访问远程 SQL Server 框时并非总是如此。 LOGON32_LOGON_INTERACTIVE

      NetworkClearText 是唯一一种始终适用于 SQL Server 连接的文本。 - 没有明文并不意味着它以不安全的方式传递凭据。

      当您在工作组中并且您需要模拟域用户时,NewCredentials 是可行的。 (未使用 SQL Server 连接测试)

      【讨论】:

        猜你喜欢
        • 2010-12-10
        • 2010-09-18
        • 1970-01-01
        • 2012-03-04
        • 2011-01-29
        • 2013-08-24
        • 1970-01-01
        • 2011-05-29
        • 2015-05-09
        相关资源
        最近更新 更多