Retaining principal inside queued background work item

大城市里の小女人 提交于 2019-12-23 07:36:18

问题


I'm using ASP.Net Web API 2 / .Net 4.5.2.

I'm trying to retain the calling principal when queueing a background work item. To that end, I'm trying to:

Thread.CurrentPrincipal = callingPrincipal;

But when I do so, I get an ObjectDisposedException:

System.ObjectDisposedException: Safe handle has been closed

How do I keep the current principal inside the background work item?
Can I make a copy of the principal somehow?

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");
        }
    });
}

回答1:


Turns out ClaimsPrincipal now has a copy constructor.

var principal = new ClaimsPrincipal(Thread.CurrentPrincipal);

This appears to resolve the issue while retaining all of the identity and claims information. The complete function follows:

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");
        }
    });
}



回答2:


The problem of your situation is that the background task being executed after the Thread.CurrentPrincipal being disposed. This happens because of ASP.NET model - the request is being handled in user context and after that all the values corresponding to the user are freed. So this is exactly happens to your identity. Try to save the information about the user and his identity to impersonate it later.

You can review a support article from Microsoft for impersonating the operations in ASP.NET sites, but I don't think that this will be helpful for you:

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();

or, may be, you can use the User.Token, something like this:

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");
    }
});

I suggest you to review your architecture design so you can find a way to move out the background operation to other context, in which the user identity would stay longer. Other way, for example, is to use the passing the current OperationContext to the Task:

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


来源:https://stackoverflow.com/questions/35460292/retaining-principal-inside-queued-background-work-item

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!