【问题标题】:Security, Thread.CurrentPrincipal, and ConfigureAwait(false)安全性、Thread.CurrentPrincipal 和 ConfigureAwait(false)
【发布时间】:2013-12-09 20:17:54
【问题描述】:

在使用 ConfigureAwait(false) 的引用库中使用 Thread.CurrentPrincipal 的声明会造成任何问题,或者 ExecutionContext 的逻辑调用上下文的流动会在那里照顾我吗? (到目前为止,我的阅读和测试表明它会)。

WebAPI 控制器操作示例:

[CustomAuthorizeThatSetsCurrentUsersClaimsToThreadCurrentContextAndHttpContextCurrentUser]
public async Task<Order> Get(int orderId)
{
    return await _orderBusinessLogicLibrary.LoadAsync(orderId); // defaults to .ConfigureAwait(true)
}

来自外部引用库的示例加载函数:

[ClaimsPrincipalPermission(
    SecurityAction.Demand,
    Operation="Read",
    Resource="Orders")]
[ClaimsPrincipalPermission(
    SecurityAction.Demand,
    Operation="Read",
    Resource="OrderItems")]
public async Task<Order> Load(int orderId)
{
    var order = await _repository.LoadOrderAsync(orderId).ConfigureAwait(false);

    // here's the key line.. assuming this lower-level function is also imposing
    // security constraints in the same way this method does, would
    // Thread.CurrentPrincipal still be correct inside the function below?
    order.Items = await _repository.LoadOrderItemsAsync(orderId).ConfigureAwait(false);
    return order;
}

此外,答案不能是“那么不要使用 ConfigureAwait(false)!”。这可能会导致其他问题,例如死锁 (Don't Block on Async Code)。

【问题讨论】:

    标签: async-await claims-based-identity synchronizationcontext executioncontext current-principal


    【解决方案1】:

    根据我的测试,即使您使用ConfigureAwait(false)Thread.CurrentPrincipal 似乎也会正确流动。以下 WebAPI 代码设置主体,然后阻塞 async 调用,强制 另一个 线程恢复 async 方法。其他线程确实继承了正确的主体。

    private async Task<string> Async()
    {
        await Task.Delay(1000).ConfigureAwait(false);
        return "Thread " + Thread.CurrentThread.ManagedThreadId + ": " + Thread.CurrentPrincipal.Identity.Name + "\n";
    }
    
    public string Get(int id)
    {
        var user = new ClaimsPrincipal(new ClaimsIdentity(
            new[]
            {
                new Claim(ClaimTypes.Name, "Bob"),
            }
        ));
        HttpContext.Current.User = user;
        Thread.CurrentPrincipal = user;
    
        var ret = "Thread " + Thread.CurrentThread.ManagedThreadId + ": " + Thread.CurrentPrincipal.Identity.Name + "\n";
    
        ret += Async().Result;
    
        return ret;
    }
    

    当我在 IISExpress 的新实例上运行此代码时,我得到:

    "Thread 7: Bob\nThread 6: Bob\n"
    

    但是,我应该指出,不建议使用ConfigureAwait(false) 来避免死锁。在 ASP.NET 上尤其如此。如果可能,请使用ConfigureAwait(false) 并且一直使用async。请注意,WebAPI 是一个完全的async 堆栈,您应该能够做到这一点。

    【讨论】:

    • 太好了,我看到了同样的结果。一个需要澄清的问题——你关于不使用ConfigureAwait(false) 来避免死锁的最后评论是特定于使用 .Result 来阻止异步方法的,对吗?所以你不应该从ConfigureAwait(true)切换到ConfigureAwait(false)来修复死锁,你应该使整个堆栈完全异步并且仍然希望在ASP.NET上等待ConfigureAwait(true)以完全恢复HttpContext等等。跨度>
    • @MichaelTrotter:ConfigureAwait 的默认值为 true。因此,如果您的 async 方法需要上下文,则根本不应该使用 ConfigureAwait。如果它不需要上下文,它应该使用ConfigureAwait(false)。我建议制作整个堆栈async,并在不需要上下文的任何方法上使用ConfigureAwait(false)
    猜你喜欢
    • 1970-01-01
    • 2017-11-08
    • 2016-05-17
    • 2014-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    相关资源
    最近更新 更多