Не удалось выполнить аутентификацию службы ASP.NET Web Api с помощью HttpClient

У меня есть служба веб-API ASP.NET, которая работает на веб-сервере с включенной аутентификацией Windows.

У меня есть клиентский сайт, построенный на MVC4, который работает на другом сайте на том же веб-сервере, который использует HttpClient для извлечения данных из службы. Этот клиентский сайт работает с включенной олицетворением личности, а также использует проверку подлинности Windows.

Веб-сервер - это Windows Server 2008 R2 с IIS 7.5.

Проблема, с которой я сталкиваюсь, заключается в том, чтобы заставить HttpClient передать текущего пользователя Windows как часть процесса проверки подлинности. Я сконфигурировал HttpClient таким образом:

var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);

Мое понимание заключается в том, что запуск сайта с олицетворением удостоверения личности включен, а затем создание клиента таким образом должно привести к аутентификации клиента службой, используя выданный личность текущего пользователя.

Этого не происходит. Фактически, клиент, похоже, вообще не аутентифицируется.

Служба настроена на использование проверки подлинности Windows, и это, похоже, работает отлично. Я могу перейти в http://server/api/shippers в своем веб-браузере и получить запрос на проверку подлинности Windows, после ввода я получаю запрошенные данные.

В журналах IIS я вижу, что запросы API получаются без аутентификации и получают ответ на вызов 401.

Документация по этому вопросу кажется редкой.

Мне нужно некоторое представление о том, что может быть неправильным или другим способом использовать проверку подлинности Windows с этим приложением.

Спасибо, Craig

Ответ 1

Я изучил исходный код HttpClientHandler (последняя версия, на которую я смог достать), и это то, что можно найти в методе SendAsync:

// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc).  Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then 
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);

Теперь, если вы проверите внутри своего кода значение SecurityContext.IsWindowsIdentityFlowSuppressed(), вы, скорее всего, получите правду. В результате метод StartRequest выполняется в новом потоке с учетными данными процесса asp.net(а не учетными данными олицетворенного пользователя).

Есть два возможных выхода из этого. Если у вас есть доступ к вашему серверу aspnet_config.config, вы должны установить следующие настройки (установка параметров в web.config, похоже, не имеет эффекта):

<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>

Если вы не можете изменить aspnet_config.config, вам нужно будет создать свой собственный HttpClientHandler для поддержки этого сценария.

ОБНОВЛЕНИЕ ОБ ИСПОЛЬЗОВАНИИ FQDN

Проблема, с которой вы столкнулись, - это функция в Windows, предназначенная для защиты от "отражений". Чтобы обойти это, вам нужно сделать белый список домена, к которому вы пытаетесь получить доступ, на машине, которая пытается получить доступ к серверу. Выполните следующие шаги:

  • Перейдите в Пуск → Выполнить → regedit
  • Найдите раздел реестра HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0.
  • Щелкните правой кнопкой мыши на нем, выберите "Создать", а затем "Многострочное значение".
  • Введите BackConnectionHostNames (ENTER).
  • Щелкните правой кнопкой мыши только что созданное значение и выберите "Изменить".
  • Поместите имя хоста для сайтов, находящихся на локальном компьютере, в поле значений и нажмите "ОК" (каждое имя хоста /FQDN должно быть в его собственной строке, без подстановочных знаков, имя должно быть точное совпадение).
  • Сохраните все и перезагрузите компьютер.

Вы можете прочитать полную статью в KB, посвященную проблеме здесь.

Ответ 2

У меня тоже была такая же проблема. Благодаря исследованиям, проведенным компанией @tpeczek, я разработал следующее решение: вместо использования HttpClient (который создает потоки и отправляет запросы async), я использовал класс WebClient, который выдает запросы в том же потоке. Это позволяет мне передать идентификатор пользователя в WebAPI из другого приложения ASP.NET.

Очевидным недостатком является то, что это не будет работать async.

var wi = (WindowsIdentity)HttpContext.User.Identity;

var wic = wi.Impersonate();
try
{
    var data = JsonConvert.SerializeObject(new
    {
        Property1 = 1,
        Property2 = "blah"
    });

    using (var client = new WebClient { UseDefaultCredentials = true })
    {
        client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
        client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
    }
}
catch (Exception exc)
{
    // handle exception
}
finally
{
    wic.Undo();
}

Примечание. Требуется пакет NuGet: Newtonsoft.Json, который является тем же самым JSON-сериализатором WebAPI, использует.

Ответ 3

Причина, по которой это не работает, заключается в том, что вам нужна аутентификация с двойным хостом.

Первым хопом является веб-сервер, получивший олицетворение с помощью проверки подлинности Windows, чтобы не работать. Но при использовании HttpClient или WebClient для проверки подлинности на другом сервере веб-сервер должен запускаться в учетной записи, которая имеет разрешение на выполнение необходимой делегирования.

Подробнее см. ниже:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx

Исправить с помощью команды "setspn":
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/ (Для выполнения этих операций вам понадобятся достаточные права доступа.)

Просто подумайте, что произойдет, если любому серверу будет разрешено пересылать ваши учетные данные, как ему заблагорассудится... Чтобы избежать этой проблемы безопасности, контроллер домена должен знать, какие учетные записи разрешено выполнять делегирование.

Ответ 4

Чтобы олицетворять оригинального (аутентифицированного) пользователя, используйте следующую конфигурацию в файле Web.config:

<authentication mode="Windows" />
<identity impersonate="true" />

При такой конфигурации ASP.NET всегда олицетворяет аутентифицированного пользователя, и весь доступ к ресурсу выполняется с использованием контекста безопасности с проверкой подлинности.