如何在托管代码中模拟用户?

时间:2010-05-11 08:00:34

标签: .net impersonation

如果有用户名和密码,我如何冒充该用户并以该用户身份运行某些代码。

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

5 个答案:

答案 0 :(得分:13)

这是我们创建的包装类,它已在多个不同的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;
    }
}

答案 1 :(得分:2)

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

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();
        }
    }

答案 2 :(得分:2)

我见过的所有示例都没有考虑到登录类型不是一个适合所有解决方案的事实。

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

NetworkClearText是唯一一个与SQL Server连接一致使用的工具。   - 没有明确的文字并不意味着它以不安全的方式传递证书。

在工作组中,您需要模拟域用户NewCredentials是有效的。 (未使用SQL Server连接进行测试)

答案 3 :(得分:1)

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

查看WindowsImpersonationContext了解更多信息(还有其他优秀的代码示例)

答案 4 :(得分:0)

请参阅Is it possible to safely get a SecureString value from VB .NET?ImpersonationHelper。该代码已准备好并且功能强大。

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