将HTTP身份验证的主体传递到另一个工作线程

时间:2010-11-29 10:27:39

标签: asp.net impersonation principal

我们的业务层服务器上有一个Web前端。

我们的Web应用程序中的某些页面实例化了很长时间的运行任务(可能长达10多分钟)。处理这些请求的方式如下: -

(在HTTP请求线程上)

  • 我们建立了与业务服务器的连接。
  • 我们创建一个新线程,使长时间运行的调用在连接对象中传递。
  • 然后HTTP请求完成,将句柄传递回浏览器
  • 浏览器会定期轮询Web服务器以获取有关长时间运行的任务进度的更新。

对业务服务器的所有请求都经过身份验证 - 连接的用户主体页面必须具有在业务服务器上调用该方法的权限。

只要我们的Web应用程序在经典模式下运行,此机制就可以正常工作。 当我们以管道模式运行时,我们在浏览器轮询时获得ObjectDisposedExceptions。

System.ObjectDisposedException: Safe handle has been closed
at System.StubHelpers.StubHelpers.SafeHandleC2NHelper(Object pThis, IntPtr CleanupWorkList)
at Microsoft.Win32.Win32Native.GetTokenInformation(SafeTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, ref UInt32 ReturnLength)
at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, ref UInt32 dwLength)
at System.Security.Principal.WindowsIdentity.get_User()
at System.Security.Principal.WindowsIdentity.GetName()
at System.Security.Principal.WindowsIdentity.get_Name()

问题似乎是用于建立连接的Windows主体在原始请求结束时被处理掉(这是可以理解的 - 事实上我很惊讶代码工作了!)。

作为解决这个问题的方法,我想知道是否有可能创建HTTP请求主体的副本并使用它来创建连接(并在长时间运行的任务完成时处置它)或者是否可能即使在处理主体之后,还要在工作线程上模拟HTTP请求原则?

更新

(根据Aliostad的问题,我的评论是错误的:测试页面确实失败了。我设法让自己充分迷惑,因为我编写了测试页面,因此它没有使用与真实(错误)代码相同的代码路径。没关系! )

我为此问题编写了“解决方法”: - 我很幸运地知道在调用业务服务器之前业务服务器逻辑将查询哪些角色/组。所以我的解决方法是根据请求的主体对这些角色的成员身份创建一个新的通用主体。长时间运行的任务使用通用主体运行。

我对这种解决方法并不是百分之百满意,因为它非常“黑客” - 即我可以看到,如果某些逻辑做了(非常明智的)检查验证委托人的身份是否经过身份验证,那很容易就会失败

所以我仍然非常感谢对此问题的任何帮助/见解。

由于

1 个答案:

答案 0 :(得分:1)

好的,这是我对此的看法。

首先,如果您创建一个线程,默认情况下,所有当前线程的安全上下文都将被复制到新线程。这个操作很重但很需要(正如你可以想象的那样,没有它,大多数事情都行不通)。如果您需要阻止它并且您不需要复制上下文,则有一种方法可以执行此操作,并且已在Richter的C#中通过CLR进行了解释。幸运的是,他已经分享了这本书here的这一部分,并基本上调用静态方法来防止上下文为flowed

ExecutionContext.SuppressFlow();

我不能认为这是在WCF中调用的,尽管使用了Reflector,我在这里找到了一次使用它:

  [SecuritySafeCritical]
    private IAsyncResult BeginGetContext(bool startListening)
    {
        Exception exception;
        do
        {
            exception = null;
            try
            {
                try
                {
                    if (ExecutionContext.IsFlowSuppressed())
                    {
                        return this.listener.BeginGetContext(this.onGetContext, null);
                    }
                    using (ExecutionContext.SuppressFlow())
                    {
                        return this.listener.BeginGetContext(this.onGetContext, null);
                    }
                }
                // .... the rest

有趣的是,这个用于3个地方,其中一个在SharedHttpTransportManager

现在所有这些可能看起来像我们发现了问题而且这是一个错误,但我非常怀疑它。

我的预感是,在两者之间发生流程回收并且上下文丢失。证明或证明这一点的方法是使用perfmon来记录所有过程循环,并找出它们之间是否存在。

我的解决方案基本上是 - 你可能不喜欢! - 简单地将一个项目插入队列(MSMQ或一个简单的数据库队列)并让一个Windows服务读取它。由于此操作非常重要,我永远不会相信IIS能够完成任务。

希望这对你有用。