.NET模拟登录是否是线程安全的?

时间:2010-09-30 18:27:58

标签: .net impersonation

如果使用以下代码来模仿其他用户,

[DllImport("advapi32.dll", SetLastError = true)]

private static extern bool

LogonUser(string lpszUsername, string lpszDomain,
          string lpszPassword, int dwLogonType,
          int dwLogonProvider, ref IntPtr phToken);
var handle = IntPtr.Zero;

const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int SecurityImpersonation = 2;
LogonUser(username, domain,
          password, LOGON32_LOGON_NETWORK,
          LOGON32_PROVIDER_DEFAULT, ref handle))

在两个不同的并发线程上,它们会相互干扰吗?即,是与线程关联的当前登录用户,还是与主机进程关联?

我正在使用登录句柄创建一个WindowsImpersonationContext对象,作为名为“Impersonator”的类型I实例中的私有状态字段(下面的代码)。因此,由于此WindowsImpersonationContext对象是此类型实例中的本地私有字段,并且每次我想模拟某些凭据集时都会创建此类型的新实例,我可以假设此WindowsImpersonationContext正在用于在块(例如

)中执行代码期间执行所有ACL验证
   using (Impersonator.Impersonate(userId, domain, password))
   {
       // Code I want to execute using supplied credentials
   }

我关心的是MSDN页面WindowsImpersonationContext 上的声明:

  

此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。

Impersonator上课:

public class Impersonator: IDisposable
{
    #region Declarations
        private readonly string username;
        private readonly string password;
        private readonly string domain;

        // This will hold the security context
        // for reverting back to the client after
        // impersonation operations are complete
        private WindowsImpersonationContext impersonationContext;
    #endregion Declarations

    #region Constructors

        public Impersonator(string UserName,
            string Domain, string Password)
        {
            username = UserName;
            domain = Domain;
            password = Password;
        }
    #endregion Constructors

    #region Public Methods
        public static Impersonator Impersonate(
            string userName, string domain, string password)
        {
            var imp = new Impersonator(userName, domain, password);
            imp.Impersonate();
            return imp;
        }

        public void Impersonate()
        {
            impersonationContext = Logon().Impersonate();
        }

        public void Undo() {
            impersonationContext.Undo();
        }
    #endregion Public Methods

    #region Private Methods
        private WindowsIdentity Logon()
        {
            var handle = IntPtr.Zero;

            const int LOGON32_LOGON_NETWORK = 3;
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int SecurityImpersonation = 2;

            // Attempt to authenticate domain user account
            try
            {
                if (!LogonUser(username, domain,
                    password, LOGON32_LOGON_NETWORK,
                    LOGON32_PROVIDER_DEFAULT, ref handle))
                    throw new LogonException(
                        "User logon failed. Error Number: " +
                        Marshal.GetLastWin32Error());

                // ----------------------------------
                var dupHandle = IntPtr.Zero;
                if (!DuplicateToken(handle,
                    SecurityImpersonation,
                    ref dupHandle))
                    throw new LogonException(
                        "Logon failed attemting to duplicate handle");

                // Logon Succeeded ! return new WindowsIdentity instance
                return (new WindowsIdentity(handle));
            }
            // Close the open handle to the authenticated account
            finally { CloseHandle(handle); }
        }

        #region external Win32 API functions
            [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 static extern bool CloseHandle(IntPtr handle);
            // --------------------------------------------

            [DllImport("advapi32.dll", CharSet = CharSet.Auto,
                 SetLastError = true)]
            public static extern bool DuplicateToken(
                IntPtr ExistingTokenHandle,
                int SECURITY_IMPERSONATION_LEVEL,
                ref IntPtr DuplicateTokenHandle);
            // --------------------------------------------
        #endregion external Win32 API functions
    #endregion Private Methods

    #region IDisposable
        private bool disposed;

        public void Dispose() { Dispose(true); }

        public void Dispose(bool isDisposing)
        {
            if (disposed)
                return;
            if (isDisposing)
                Undo();
            // -----------------
            disposed = true;
            GC.SuppressFinalize(this);
        }

        ~Impersonator() {
            Dispose(false);
        }

    #endregion IDisposable
}

2 个答案:

答案 0 :(得分:5)

它与任何东西无关。您拥有的句柄是登录句柄。完成后,使用该句柄在线程上模拟用户(通过调用WindowsIdentity.Impersonate)或进程(通过CreateProcess API function或模拟线程,然后创建新的{模仿用户时的{3}}实例。

无论哪种方式,调用Process都不执行任何模拟,它只是为您提供了一个您需要执行模拟的用户句柄(假设您拥有该权限)。

答案 1 :(得分:1)

我认为你的意思是:“如果线程A冒充Joe,同时线程B冒充Fred,那么一切正常。”

即。将一个工作线程作为Joe(而不是Fred),反之亦然。答案是肯定的;模仿属于线程。