【问题标题】:WindowsIdentity.Impersonate() confusionWindowsIdentity.Impersonate() 混淆
【发布时间】:2015-02-02 10:46:02
【问题描述】:

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

问题是Request线程(父线程)可以访问数据库,而子线程不能访问。

解决这个问题。我将主线程的 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();

【问题讨论】:

  • 您没有说您使用的是哪个版本的 IIS。带有集成管道的 IIS7 或更高版本不再直接在配置中支持模拟,主要是因为异步请求的问题(即,当您使用 asyc/await 时,请求可以在不再具有模拟的不同线程上恢复)。所以我不确定你所说的“启用模拟”是什么意思。

标签: c# .net multithreading impersonation


【解决方案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())
{
    .
    .
    .
}

【讨论】:

  • 如果App pool线程被模拟然后退出而不调用undo,并且该线程被重用,即使你没有调用模拟,它也将保持被模拟的身份。我可以使用一些测试代码来确认这一点。
猜你喜欢
  • 2014-07-09
  • 1970-01-01
  • 2014-05-06
  • 2011-05-11
  • 2012-06-02
  • 2012-08-15
  • 2019-06-26
  • 2012-04-18
  • 2023-03-29
相关资源
最近更新 更多