Установка IPrincipal в обработчике сообщений или фильтре не входит в контроллер правильно

У меня есть проект Web API 2, и мы используем базовую авторизацию. Клиент отправляет имя пользователя: пароль в заголовке авторизации — мы извлекаем его и подключаемся к серверу LDAP для

  1. проверить пользователя
  2. подтвердите свой пароль, а затем
  3. получить значение атрибута из своего ответа LDAP и передать его в IPrincipal запроса.

Проблема в том, что когда я обращаюсь к IPrincipal в контроллере через System.Web.Http.ApiController.User.Identity.Name, я вижу, что переданное значение не всегда правильно!

Пример: либо в обработчике сообщений, либо в фильтре я установил IPrincipal следующим образом:

    public class BasicAuthenticationAttribute : Attribute, IAuthenticationFilter
    {

        ...other code...

        private IPrincipal ReturnPrincipal(string UserID, List<string> roles)
        {
            roles.Add("SomeRole");

            // UserID = 7144 or 8899 (load testing)
            var identity = new GenericIdentity(UserID, "Basic");
            var principal = new GenericPrincipal(identity, roles.ToArray());

            return principal;
        }
    }

Редактировать: вот как мы устанавливаем принцип в фильтре:

        var principal = await AuthenticateAsync(UserID, password, cancellationToken);

        if (principal == null)
        {
            // Authentication was attempted but failed. Set ErrorResult to indicate an error.
            context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
        }
        else
        {
            // Authentication was attempted and succeeded. Set Principal to the authenticated user.
            context.Principal = principal;
        }

Позже в жизненном цикле запроса в контроллере я пытаюсь получить доступ к этому значению следующим образом:

    protected bool IsAuthorized(string UserID)
    {
        return User.Identity.Name == UserID;
    }

Итак, здесь происходит следующее: я получаю User.Identity.Name от ApiController.User.Identity.Name в разделе нагрузочное тестирование, я отправить 2 параллельных запроса с нитями в ОДНО ВРЕМЯ - когда я это делаю, объект запроса показывает правильный идентификатор в URL-адресе, однако IPrincipal, установленный в фильтре, НЕ является правильным. Это фиксированный тест с ожидаемыми результатами, поэтому этого никогда не должно происходить. Если это поразить звонки, проблема не возникает! Если звонки отправляются одновременно, проблема возникает в 100% случаев.

Вот как я вызываю службу:

            var task1 = Task.Run(() => CallServerInParallel(requests2, api));
            var task2 = Task.Run(() => CallServerInParallel2(requests2, api));

            Task.WaitAll(task1, task2);

...

    private static void CallServerInParallel2(List<int> requests2, string api)
    {
        Parallel.ForEach(requests2, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, async p =>
        {
            var client = new RemoteClient(api);
            client.AddAuthHeader("d2JsMTE4NTgzOUBbdGVhY2NlcHQuY29tOnRlc3QxMjM0");
            var response = await client.Get<dynamic>("CustomerSite/5537");
        });
    }

    private static void CallServerInParallel2(List<int> requests2, string api)
    {
        Parallel.ForEach(requests2, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, async p =>
        {
            var client = new RemoteClient(api);
            client.AddAuthHeader("d2JsMTE4NTgzOUBkdGVhY1NlcHQuY29tOnRlc3QxMjM0");
            var response = await client.Get<dynamic>("CustomerSite/5538");
        });
    }

Как я могу получить доступ к правильному IPrincipal.User.Identity.Name для запроса?


person Steve Stokes    schedule 03.02.2016    source источник
comment
Как ReturnPrincipal вызывается? Где вы на самом деле устанавливаете принципала в контексте?   -  person    schedule 03.02.2016
comment
Добавление к теме выше, так как это фрагмент кода   -  person Steve Stokes    schedule 03.02.2016


Ответы (1)


Как указано здесь: Настройка принципала

Я обошел эту проблему, убедившись, что в моем фильтре после установки принципала контекста я также делаю следующее

Если ваше приложение выполняет какую-либо пользовательскую логику аутентификации, вы должны установить принципала в двух местах:

  • Thread.CurrentPrincipal. Это свойство является стандартным способом установки принципала потока в .NET.
  • HttpContext.Current.User. Это свойство специфично для ASP.NET.

В следующем коде показано, как установить участника:

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

Для веб-хостинга вы должны установить принципала в обоих местах; в противном случае контекст безопасности может стать несогласованным. Однако для самостоятельного размещения HttpContext.Current имеет значение null. Поэтому, чтобы убедиться, что ваш код не зависит от хоста, проверьте значение null перед назначением HttpContext.Current, как показано.

var principal = await AuthenticateAsync(UserID, password, cancellationToken);

if (principal == null) {
    // Authentication was attempted but failed. Set ErrorResult to indicate an error.
    context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
} else {
    // Authentication was attempted and succeeded. Set Principal to the authenticated user.
    context.Principal = principal;
    SetPrincipal(principal);
}
person Nkosi    schedule 03.02.2016
comment
К сожалению, это не решение. Я только что проверил это, и это не решило проблему. :( - person Steve Stokes; 04.02.2016