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

Ari*_*edi 8 .net c# impersonation task-parallel-library parallel.foreach

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

事实上是一个异常,AggregateException并且被抛入一个方法,该方法Parallel.ForEach用于枚举列表和正文内部,它尝试访问该文件夹,但此时模拟上下文被更改,工作线程作为应用程序池运行身份,无权访问该文件夹,因此异常.

为了证实这一点,我查看了身体之前和之内的过程身份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);
});
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,一些线程作为模拟标识运行,一些线程作为应用程序池运行(在本例中LocalSystem),并且似乎没有模式.Call Stack窗口中的前一帧也进入非托管状态kernel32.dll,这使我认为CLR在将其委托给操作系统之前不验证上下文.

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

Yuv*_*kov 4

与类不同TaskParallel似乎没有捕获ExecutionContext您当前正在运行的(反过来捕获SecurityContext持有 的WindowsIdentity)。它使用当前线程内可用的一个。

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

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);
   }
});
Run Code Online (Sandbox Code Playgroud)

  • 我认为“并行”也使用当前执行线程作为其线程池的一部分。这就是为什么我们“有时”会看到正确的身份。 (2认同)