Безопасность, Thread.CurrentPrincipal и ConfigureAwait(false)

Будет ли использование утверждений Thread.CurrentPrincipal в ссылочной библиотеке, которая использует ConfigureAwait(false), создавать какие-либо проблемы, или будет ли забота обо мне, связанная с потоком контекста логического вызова 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) тогда!". Это может вызвать другие проблемы, такие как тупик ( не блокировать в асинхронном коде).

1 ответ

Решение

Из моих тестов выясняется, что 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;
}

Когда я запускаю этот код на новом экземпляре IISExpress, я получаю:

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

Тем не менее, я должен отметить, что с помощью ConfigureAwait(false) избегать тупиковых ситуаций не рекомендуется. Это особенно верно в ASP.NET. Если это вообще возможно, используйте ConfigureAwait(false) а также использовать async весь путь. Обратите внимание, что WebAPI является полностьюasync стек, и вы должны быть в состоянии сделать это.