将主体保留在排队的后台工作项中

本文关键字:后台 工作 排队 主体 保留 | 更新日期: 2023-09-27 18:35:06

我正在使用 ASP.Net Web API 2/.Net 4.5.2。

我尝试在对后台工作项进行排队时保留调用主体。 为此,我正在尝试:

Thread.CurrentPrincipal = callingPrincipal;

但是当我这样做时,我得到一个 ObjectDisposedException:

System.ObjectDisposedException: 安全句柄已关闭

如何将当前主体保留在后台工作项中?
我可以以某种方式复制校长吗?

public void Run<T>(Action<T> action)
{
    _logger.Debug("Queueing background work item");
    var callingPrincipal = Thread.CurrentPrincipal;
    HostingEnvironment.QueueBackgroundWorkItem(token =>
    {
        try
        {
            // UNCOMMENT - THROWS EXCEPTION
            // Thread.CurrentPrincipal = callingPrincipal;
            _logger.Debug("Executing queued background work item");
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        catch (Exception ex)
        {
            _logger.Fatal(ex);
        }
        finally
        {
            _logger.Debug("Completed queued background work item");
        }
    });
}

将主体保留在排队的后台工作项中

事实证明ClaimsPrincipal现在有一个复制构造函数。

var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);

这似乎可以解决问题,同时保留所有身份和声明信息。 完整的功能如下:

public void Run<T>(Action<T> action)
{
    _logger.Debug("Queueing background work item");
    var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);
    HostingEnvironment.QueueBackgroundWorkItem(token =>
    {
        try
        {
            Thread.CurrentPrincipal = principal;
            _logger.Debug("Executing queued background work item");
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        catch (Exception ex)
        {
            _logger.Fatal(ex);
        }
        finally
        {
            _logger.Debug("Completed queued background work item");
        }
    });
}

您的情况的问题是后台任务在Thread.CurrentPrincipal被释放后执行。发生这种情况是因为 ASP.NET 模型 - 请求在用户上下文中处理,之后释放与用户对应的所有值。所以这正是你的身份。尝试保存有关用户及其标识的信息,以便稍后模拟它。

您可以查看 Microsoft 的支持文章,以模拟 ASP.NET 站点中的操作,但我认为这对您没有帮助:

System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = 
    ((System.Security.Principal.WindowsIdentity)callingPrincipal.Identity).Impersonate();
//Insert your code that runs under the security context of the authenticating user here.
impersonationContext.Undo();

或者,也许,您可以使用User.Token,如下所示:

HostingEnvironment.QueueBackgroundWorkItem(token =>
{
    try
    {
        _logger.Debug("Executing queued background work item");
        using (HostingEnvironment.Impersonate(callingPrincipal.Identity))
        {
            using (var scope = DependencyResolver.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();
                action(service);
            }
        }
        // UNCOMMENT - THROWS EXCEPTION
        // Thread.CurrentPrincipal = callingPrincipal;
    }
    catch (Exception ex)
    {
        _logger.Fatal(ex);
    }
    finally
    {
        _logger.Debug("Completed queued background work item");
    }
});

我建议您查看体系结构设计,以便找到一种方法将后台操作移动到其他上下文,其中用户标识将保留更长时间。例如,另一种方法是使用将当前OperationContext传递给Task

// store local operation context
var operationContext = OperationContext.Current;
TaskFactory.StartNew(() =>
{
    // initialize the current operation context
    OperationContext.Current = operationContext;
    action();
})