Parallel.ForEach()更改模拟上下文

今天我们将新创建的ASP.NET应用程序部署到服务器,很快我们意识到存在一个与安全相关的奇怪问题导致应用程序崩溃。 这是一个内部应用程序,我们使用Impersonation来管理用户访问资源的方式。 但是,当用户尝试访问他们完全控制的文件夹时,应用程序会抛出“拒绝访问”exception。

exception实际上是一个AggregateException并且被抛出一个方法,该方法使用Parallel.ForEach枚举列表并在正文内部,它尝试访问该文件夹,但此时模拟上下文被更改并且工作线程运行作为应用程序池的标识,它无权访问该文件夹,因此exception。

为了确认这一点,我查看了Parallel.ForEach主体内部和内部的进程标识:

 string before = WindowsIdentity.GetCurrent().Name; Debug.WriteLine("Before Loop: {0}", before); Parallel.ForEach(myList, currentItem => { string inside = WindowsIdentity.GetCurrent().Name; Debug.WriteLine("Inside Loop: {0} (Worker Thread {1})", inside, Thread.CurrentThread.ManagedThreadId); }); 

当我运行应用程序时,这是打印出来的:

 Before Loop: MyDomain\ImpersonatedUser Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 8) Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 6) Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7) Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 9) Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 10) Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7) Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 6) Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7) 

正如您所看到的,一些线程作为模拟身份运行,一些线程作为应用程序池(在本例中为LocalSystem )运行,并且似乎没有模式。 Call Stack窗口中的前一帧也转到非托管kernel32.dll ,这让我觉得CLR在将其委托给操作系统之前没有validation上下文。

知道为什么会这样吗? 这是一个已知的问题/错误吗?

Task类不同, Parallel似乎没有捕获您当前正在运行的ExecutionContext (它反过来捕获保存WindowsIdentitySecurityContext )。 它使用当前Thread内部的一个可用。

您必须明确捕获所需的上下文:

 IntPtr token = WindowsIdentity.GetCurrent().Token; Parallel.ForEach(myList, currentItem => { using (WindowsIdentity.Impersonate(token)) { string inside = WindowsIdentity.GetCurrent().Name; Debug.WriteLine("Inside Loop: {0} (Worker Thread {1})", inside, Thread.CurrentThread.ManagedThreadId); } }); 

Windows中的整个模拟概念是每线程概念。 您会看到,在创建新线程时,它会inheritance进程的权限令牌。 但是, 进程不同 ,线程也有一个模拟令牌。 当您调用WindowsIdentity.Impersonate(令牌)时,您在调用线程上设置模拟令牌。

此外,当您调用Impersonate时,您将线程的主要令牌指针设置为指向模拟令牌,而不是进程的主令牌,这是默认值。

在WinAPI中获得更多的理解和知识会让您知道您的服务中发生的事情预期的行为。