WindowsIdentity.Impersonate()混淆

时间:2014-12-04 14:02:27

标签: c# .net multithreading impersonation

我有一个由IIS托管的Web应用程序。它配置了表单身份验证和匿名身份验证,并启用了模拟。 应用程序池帐户是网络服务。匿名帐户是Costa。 Costa可以访问数据库。 NetworkService无法访问数据库。

问题是请求线程(父线程)可以访问数据库但子线程不能。

解决这个问题。我将主线程的windows身份对象发送到子线程,然后我调用Impersonate()。模拟意味着"使用模拟帐户分配当前线程Windows身份。 我的问题:这是一个好习惯吗?有风险吗?

\\Request thread code (Parent thread)

\\WindowsIdentity.GetCurrent() return Costa identity (impersonated)
requestFields.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
ThreadPool.QueueUserWorkItem(LogRequest, requestFields);

-

\\ Sub thread code that works
RequestFields requestFields = (RequestFields)obj;
HttpRequest httpRequest = requestFields.Request;

var impersonationContext = ((WindowsIdentity)requestFields.CurrentPrincipal.Identity).Impersonate();
.
.
.
impersonationContext.Undo();

1 个答案:

答案 0 :(得分:1)

工作线程没有自动模仿用户的请求线程的原因是.NET线程池管理其线程的创建和重用。如果您通过下面的示例代码自己创建了该线程,我希望它能够自动继承安全上下文(包括模拟),但您也不会重新使用会增加执行成本的线程(参见Thread vs ThreadPool有关两者之间的差异以及使用线程池线程的好处的详细信息。)

因为你知道IIS会模拟用户,并且根据http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx它使用线程池来处理它的请求,我会得出结论,只要你采取步骤,在线程池线程上进行模拟并不危险尽可能确保即使在特殊情况下也会撤消假冒行为。如果未撤消模拟,则存在使用线程池(您的,其他库或.NET框架本身)的其他代码被分配给使用某个随机标识而不是应用程序池标识的线程的风险。

我不确定RequestFields类是什么(快速搜索似乎表明它不是.NET框架的一部分),所以我不明白为什么有必要将WindowsIdentity包装在WindowsPrincipal中,因为你不要使用Identity以外的任何属性,它会强制你在另一个线程上进行强制转换。如果您拥有此类并且可以更改它,我建议您更改CurrentPrincipal属性以直接获取WindowsIdentity,以便可以在没有不必要的WindowsPrincipal包装的情况下传递它。

我认为您可以将当前的WindowsIdentity传递给另一个线程池线程并按照您的方式调用Impersonate。但是,您绝对应该将impersonationContext包装在using块中,或者将Undo包装在try / finally块的finally部分中,该块在调用Impersonate时启动,以确保即使在异常或线程中止的情况下也会撤消模拟。您还应确保WindowsIdentity.GetCurrent()创建的WindowsIdentity副本也已被处置,因此对标识后面的非托管用户令牌的所有引用都将被确定性地关闭(即不是通过垃圾收集器完成)。

创建新主题的示例代码:

Thread myThread = new Thread(LogRequest);

// the CLR will not wait for this thread to complete if all foreground threads
// have been terminated; this mimics the thread pool since all thread pool threads
//are designated as background threads
myThread.IsBackground = true;

myThread.Start(requestFields);

使用using块正确处理WindowsImpersonationContext和WindowsIdentity对象的示例代码(impersonationContext.Dispose将调用Undo):

using (var identity = (WindowsIdentity)requestFields.CurrentPrincipal.Identity)
using (var impersonationContext = identity.Impersonate())
{
    .
    .
    .
}