安全性、Thread.CurrentPrincipal 和 ConfigureAwait(false)

10

如果在使用ConfigureAwait(false)的引用库中使用Thread.CurrentPrincipal的声明是否会带来任何问题,或者ExecutionContext的逻辑调用上下文的流动是否会在那里照顾我?(我的阅读和测试迄今表明它会)。

示例WebAPI Controller操作:

[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)!”这样会导致其他问题,比如死锁(不要在异步代码中阻塞)。

1个回答

13

从我的测试结果来看,即使你使用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实例上运行这段代码时,我会得到以下结果:

When I run this code on a new instance of IISExpress, I get:

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

但是,我应该指出的是,使用ConfigureAwait(false)来避免死锁并不被推荐,特别是在ASP.NET上更为如此。如果可能的话,请始终使用ConfigureAwait(false)并且一路使用async。请注意,WebAPI是完全异步的堆栈,您应该能够做到这一点。


太好了,我看到了相同的结果。有一个澄清的问题--您最后关于不使用ConfigureAwait(false)避免死锁的评论是特定于使用.Result在异步方法上阻塞吗?所以,你不应该切换从ConfigureAwait(true)ConfigureAwait(false)来解决死锁,你应该使整个栈完全异步,并且仍然希望ASP.NET等待中的ConfigureAwait(true)以完全恢复HttpContext等。 - Madeline Trotter
3
ConfigureAwait 的默认值是 true。因此,如果你的 async 方法需要上下文,则应该完全不使用 ConfigureAwait。如果它不需要上下文,则应该使用 ConfigureAwait(false)。我建议让整个堆栈都是 async,并且在任何不需要上下文的方法上也使用 ConfigureAwait(false) - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接