Является ли внедрение олицетворения .NET потокобезопасным?

Если для олицетворения другого пользователя используется следующий код:

[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))

на двух разных одновременных потоках, будут ли они мешать друг другу? I.e., является в настоящее время зарегистрированным пользователем, связанным с потоком, или с хост-процессом?

Я использую дескриптор входа для создания объекта WindowsImpersonationContext в качестве поля частного состояния в экземпляре типа я с именем "Impersonator" (код ниже). Таким образом, поскольку этот объект WindowsImpersonationContext является локальным частным полем в экземпляре этого типа, и каждый экземпляр этого типа создается каждый раз, когда я хочу выдавать себя за некоторый набор учетных данных, я могу предположить, что этот WindowsImpersonationContext - это то, что используется для выполнять все проверки ACL во время выполнения кода внутри блока, например

   using (Impersonator.Impersonate(userId, domain, password))
   {
       // Code I want to execute using supplied credentials
   }

Что меня беспокоит, так это выражение на странице MSDN WindowsImpersonationContext, в котором говорится:

Все публичные статические (Shared in Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не гарантируют безопасность потоков.

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
}

Ответы

Ответ 1

Это не связано ни с чем. Ручка, с которой вы работаете, является ручкой входа. После этого вы используете этот дескриптор для олицетворения пользователя в потоке (путем вызова WindowsIdentity.Impersonate) или для процесса (либо через CreateProcess Функция API или олицетворяя нить, а затем создавая новый Process при выдаче себя за пользователя).

В любом случае вызов функции LogonUser API не выполняет какого-либо олицетворения, он просто дает вам дескриптор пользователя, который вы необходимо выполнить олицетворение (при условии, что у вас есть привилегия).

Ответ 2

Я думаю, вы имеете в виду: "Если нить A олицетворяет Джо, и в то же время поток B олицетворяет Фреда, все будет работать нормально".

т.е. будет работать как Joe (а не Fred) и наоборот. Ответ - да; олицетворение принадлежит потоку.