zoukankan      html  css  js  c++  java
  • 如何在C#程序中模拟域帐户进行登录操作 (转载)

    .NET Core


    .NET Core也支持用PInvoke来调用操作系统底层的Win32函数

    首先要在项目中下载Nuget包:System.Security.Principal.Windows

    代码加注释:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    
    
    namespace NetCorePrincipal
    {
        public class WindowsLogin : IDisposable
        {
            protected const int LOGON32_PROVIDER_DEFAULT = 0;
            protected const int LOGON32_LOGON_INTERACTIVE = 2;
    
            public WindowsIdentity Identity = null;
            protected IntPtr m_accessToken;
    
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            private extern static bool CloseHandle(IntPtr handle);
    
    
            // AccessToken ==> this.Identity.AccessToken
            //public Microsoft.Win32.SafeHandles.SafeAccessTokenHandle AT
            //{
            //    get
            //    {
            //        var at = new Microsoft.Win32.SafeHandles.SafeAccessTokenHandle(this.m_accessToken);
            //        return at;
            //    }
            //}
    
            public WindowsLogin(string username, string domain, string password)
            {
                Login(username, domain, password);
            }
    
    
            public void Login(string username, string domain, string password)
            {
                if (this.Identity != null)
                {
                    this.Identity.Dispose();
                    this.Identity = null;
                }
    
    
                try
                {
                    this.m_accessToken = new IntPtr(0);
                    Logout();
    
                    this.m_accessToken = IntPtr.Zero;
                    bool logonSuccessfull = LogonUser(
                       username,
                       domain,
                       password,
                       LOGON32_LOGON_INTERACTIVE,
                       LOGON32_PROVIDER_DEFAULT,
                       ref this.m_accessToken);
    
                    if (!logonSuccessfull)
                    {
                        int error = Marshal.GetLastWin32Error();
                        throw new System.ComponentModel.Win32Exception(error);
                    }
                    Identity = new WindowsIdentity(this.m_accessToken);
                }
                catch
                {
                    throw;
                }
    
            }
    
    
            public void Logout()
            {
                if (this.m_accessToken != IntPtr.Zero)
                    CloseHandle(m_accessToken);
    
                this.m_accessToken = IntPtr.Zero;
    
                if (this.Identity != null)
                {
                    this.Identity.Dispose();
                    this.Identity = null;
                }
    
            } // End Sub Logout 
    
    
            public void Dispose()
            {
                Logout();
            } // End Sub Dispose 
    
        } // End Class WindowsLogin 
    
    
    
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");
    
                using (WindowsLogin wi = new WindowsLogin("uid", "serverdomain", "pwd"))
                {
                    WindowsIdentity.RunImpersonated(wi.Identity.AccessToken, () =>
                    {
                        Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");
    
                        //假定要操作的文件路径是10.0.250.11上的d:	xt.txt文件可以这样操作
                        FileInfo file = new FileInfo(@"\10.0.250.11d$	xt.txt");
                        //想做什么操作就可以做了
                    });
                }
    
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");
    
                Console.WriteLine("Press any key to quit...");
                Console.ReadKey();
            }
        }
    }

    .NET Framework


    代码加注释:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    
    
    namespace NetFrameworkPrincipal
    {
        public class WindowsLogin : IDisposable
        {
            protected const int LOGON32_PROVIDER_DEFAULT = 0;
            protected const int LOGON32_LOGON_INTERACTIVE = 2;
    
            public WindowsIdentity Identity = null;
            protected IntPtr m_accessToken;
    
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            private extern static bool CloseHandle(IntPtr handle);
    
    
            // AccessToken ==> this.Identity.AccessToken
            //public Microsoft.Win32.SafeHandles.SafeAccessTokenHandle AT
            //{
            //    get
            //    {
            //        var at = new Microsoft.Win32.SafeHandles.SafeAccessTokenHandle(this.m_accessToken);
            //        return at;
            //    }
            //}
    
            public WindowsLogin(string username, string domain, string password)
            {
                Login(username, domain, password);
            }
    
    
            public void Login(string username, string domain, string password)
            {
                if (this.Identity != null)
                {
                    this.Identity.Dispose();
                    this.Identity = null;
                }
    
    
                try
                {
                    this.m_accessToken = new IntPtr(0);
                    Logout();
    
                    this.m_accessToken = IntPtr.Zero;
                    bool logonSuccessfull = LogonUser(
                       username,
                       domain,
                       password,
                       LOGON32_LOGON_INTERACTIVE,
                       LOGON32_PROVIDER_DEFAULT,
                       ref this.m_accessToken);
    
                    if (!logonSuccessfull)
                    {
                        int error = Marshal.GetLastWin32Error();
                        throw new System.ComponentModel.Win32Exception(error);
                    }
                    Identity = new WindowsIdentity(this.m_accessToken);
                }
                catch
                {
                    throw;
                }
    
            }
    
    
            public void Logout()
            {
                if (this.m_accessToken != IntPtr.Zero)
                    CloseHandle(m_accessToken);
    
                this.m_accessToken = IntPtr.Zero;
    
                if (this.Identity != null)
                {
                    this.Identity.Dispose();
                    this.Identity = null;
                }
    
            } // End Sub Logout 
    
    
            public void Dispose()
            {
                Logout();
            } // End Sub Dispose 
    
        } // End Class WindowsLogin 
    
    
    
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");
    
                using (WindowsLogin wi = new WindowsLogin("uid", "serverdomain", "pwd"))
                {
                    //WindowsIdentity.RunImpersonated(wi.Identity.AccessToken, () =>
                    //{
                    //    //假定要操作的文件路径是10.0.250.11上的d:	xt.txt文件可以这样操作
                    //    FileInfo file = new FileInfo(@"\10.0.250.11d$	xt.txt");
                    //    //想做什么操作就可以做了
                    //});
    
                    using (wi.Identity.Impersonate())
                    {
                        Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");
    
                        //假定要操作的文件路径是10.0.250.11上的d:	xt.txt文件可以这样操作
                        FileInfo file = new FileInfo(@"\10.0.250.11d$	xt.txt");
                        //想做什么操作就可以做了
                    }
                }
    
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");
    
                Console.WriteLine("Press any key to quit...");
                Console.ReadKey();
            }
        }
    }

    模拟域帐户之后,就有了模拟用户的权限,这里千万要注意安全!

    模拟的域帐户是和线程绑定的


    需要注意的一点是,模拟的域账户是和C#中的线程绑定的。

    例如下面.NET Core控制台项目中,我们在Program类的Main方法中,模拟域账户UserB后,再启动一个新的线程去查看当前用户,还是显示的是UserB,代码如下所示:

    using System;
    using System.Security.Principal;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace NetCorePrincipal
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserA
    
                //模拟域帐户UserB
                using (WindowsLogin wi = new WindowsLogin("UserB", "Domain", "1qaz!QAZ"))
                {
                    WindowsIdentity.RunImpersonated(wi.Identity.AccessToken, () =>
                    {
                        Console.WriteLine("Impersonation started.");
                        Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserB
    
                        //启动另一个线程查看当前用户
                        Task.Run(() =>
                        {
                            Console.WriteLine($"Current user in another thread is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserB
                        }).Wait();//等待启动的线程执行完毕
    
                        Console.WriteLine("Impersonation finished.");
                    });
                }
    
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserA
    
                Console.WriteLine("Press any key to quit...");
                Console.ReadKey();
            }
        }
    }

    输出结果如下所示:

    Current user is : DomainUserA
    Impersonation started.
    Current user is : DomainUserB
    Current user in another thread is : DomainUserB
    Impersonation finished.
    Current user is : DomainUserA
    Press any key to quit...

    但是接下来我们更改Program类的Main方法代码,在模拟域账户UserB前先启动一个新的线程,然后模拟域账户UserB后,在新启动的线程中查看当前用户,会显示当前用户是UserA,代码如下所示:

    using System;
    using System.Security.Principal;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace NetCorePrincipal
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserA
    
                //启动另一个线程查看当前用户
                var newTask = Task.Run(() =>
                {
                    Thread.Sleep(3000);
                    Console.WriteLine($"Current user in another thread is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserA
                });
    
                //模拟域帐户UserB
                using (WindowsLogin wi = new WindowsLogin("UserB", "Domain", "1qaz!QAZ"))
                {
                    WindowsIdentity.RunImpersonated(wi.Identity.AccessToken, () =>
                    {
                        Console.WriteLine("Impersonation started.");
                        Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserB
    
                        newTask.Wait();//等待启动的线程执行完毕
    
                        Console.WriteLine("Impersonation finished.");
                    });
                }
    
                Console.WriteLine($"Current user is : {WindowsIdentity.GetCurrent().Name}");//输出当前用户为UserA
    
                Console.WriteLine("Press any key to quit...");
                Console.ReadKey();
            }
        }
    }

    输出结果如下所示:

    Current user is : DomainUserA
    Impersonation started.
    Current user is : DomainUserB
    Current user in another thread is : DomainUserA
    Impersonation finished.
    Current user is : DomainUserA
    Press any key to quit...

    由此可见模拟的域帐户实际上是和线程绑定的,新启动的线程会继承启动它的线程的域帐户。

    • 如果线程A的当前域帐户是UserA,然后线程A启动了线程B,那么启动的线程B当前域帐户也会是UserA。
    • 如果线程A的当前域帐户是UserA,然后线程A使用本文所述方法模拟域帐户UserB,再启动线程B,那么启动的线程B当前域帐户会是UserB。
  • 相关阅读:
    c++ 中 pair 的 使用方法
    初窥c++11:lambda函数及其用法
    HDU2089-不要62
    算法训练 K好数
    点评删除和编辑
    事务
    SQL Function 自定义函数
    常用CSS实例
    分页显示数据
    开发教程指南
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/10029870.html
Copyright © 2011-2022 走看看