Security,Thread.CurrentPrincipal和ConfigureAwait(false)

Mad*_*ter 8 claims-based-identity synchronizationcontext async-await current-principal executioncontext

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

示例WebAPI控制器操作:

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

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

[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;
}
Run Code Online (Sandbox Code Playgroud)

此外,答案不能是"那么不要使用ConfigureAwait(false)!".这可能会导致其他问题,例如死锁(请勿在异步代码上阻止).

Ste*_*ary 13

从我的测试来看Thread.CurrentPrincipal,即使您使用,它也会正常流动ConfigureAwait(false).以下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;
}
Run Code Online (Sandbox Code Playgroud)

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

"Thread 7: Bob\nThread 6: Bob\n"
Run Code Online (Sandbox Code Playgroud)

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

  • @MichaelTrotter:`ConfigureAwait`的默认值是`true`.因此,如果你的`async`方法需要上下文,它根本就不应该使用`ConfigureAwait`.如果它不需要上下文,它应该使用`ConfigureAwait(false)`.我建议在任何不需要上下文的方法上创建整个堆栈`async`并使用`ConfigureAwait(false)`. (3认同)