Невозможно пройти аутентификацию в службе веб-API ASP.NET с помощью 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 с этим приложением.

Спасибо, Крейг


person Craig Anderson    schedule 25.04.2012    source источник


Ответы (4)


Я исследовал исходный код 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, которая предназначена для защиты от «атак отражения». Чтобы обойти это, вам необходимо внести в белый список домен, к которому вы пытаетесь получить доступ на машине, которая пытается получить доступ к серверу. Выполните следующие шаги:

  1. Зайдите в Пуск -> Выполнить -> regedit
  2. Найдите HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 раздел реестра.
  3. Щелкните его правой кнопкой мыши, выберите Создать, а затем Многострочное значение.
  4. Введите BackConnectionHostNames (ENTER).
  5. Щелкните правой кнопкой мыши только что созданное значение и выберите Изменить.
  6. Поместите имена хостов для сайтов, которые находятся на локальном компьютере, в поле значения и нажмите OK (каждое имя хоста / полное доменное имя должно быть в отдельной строке, без подстановочных знаков, имя должно быть точным совпадением).
  7. Сохраните все и перезапустите машину

Вы можете прочитать полную статью базы знаний об этой проблеме здесь.

person tpeczek    schedule 25.04.2012
comment
Я пошел дальше и установил эти значения без каких-либо изменений в поведении. Странно то, что я вижу, как клиент обращается к серверу и получает ответ 401. Кажется, что HttpClient НЕ отправляет никакой информации для аутентификации, просто получает 401 и завершает работу. Есть дополнительные мысли? - person Craig Anderson; 25.04.2012
comment
Кроме того, я изменил идентификатор пула приложений, в котором работает клиент, на учетную запись Windows, имеющую доступ к службе. Это не приводит к изменениям. Я потерялся в этом? - person Craig Anderson; 25.04.2012
comment
@CraigAnderson Можете ли вы проверить значения System.Security.Principal.WindowsIdentity.GetCurrent () и SecurityContext.IsWindowsIdentityFlowSuppressed () непосредственно перед отправкой запроса с помощью HttpClient? Если дело доходит до потока запросов, это правильно. Установка для PreAuthenticate значения true заставляет HttpClient отправлять заголовок авторизации с начальным запросом. Если ответ - 401, он не предпринимает никаких дальнейших попыток аутентификации, поскольку учетные данные, которые можно отправить, уже были отклонены. - person tpeczek; 26.04.2012
comment
@tpeczek Я включил дополнительное логирование в приложении. Прямо перед тем, как клиент отправит запрос, я вижу, что текущий идентификатор Windows на самом деле является зарегистрированным пользователем, а значение IsWindowsIndentityFlowSuppressed установлено в false. Это наводит на мысль, что httpclient передает аутентификацию службе? - person Craig Anderson; 26.04.2012
comment
Что ж, я чувствую, что мы подтвердили, что клиент отправляет запрос, когда пользователь вошел в систему. Служба задыхается еще до того, как она выполнит первую строку кода. Это как если бы служба не видит и не понимает переданную ей проверку подлинности Windows. Я не знаю, как понять, что здесь происходит, чтобы лучше понять. Это просто отказ в доступе, вероятно, потому, что он не может соответствовать учетным данным Windows. Это проблема с сертификатом или что-то подобное? - person Craig Anderson; 26.04.2012
comment
@CraigAnderson Да, похоже, HttpClient должен иметь возможность отправлять правильные учетные данные. В этом случае нам нужно проверить точные заголовки, которыми обмениваются клиент и сервер. Пожалуйста, попробуйте Fiddler или другой аналогичный инструмент для отладки запросов. - person tpeczek; 26.04.2012
comment
@CraigAnderson Еще одна мысль - вы уверены, что не получаете никаких исключений из HttpClient, и он просто терпит неудачу? - person tpeczek; 26.04.2012
comment
Мы обнаружили, что две приведенные выше настройки действительно были частью решения. Еще мы обнаружили, что система работает нормально, если мы подключаемся от клиента к службе на localhost / api. Это не работает с использованием полного доменного имени. Сейчас система работает, но по-настоящему не работает, пока мы не выясним, почему не работает полное доменное имя. tpeczek, есть мысли? - person Craig Anderson; 27.04.2012
comment
@CraigAnderson Я только что обновил ответ в теме доступа по FQDN. - person tpeczek; 27.04.2012
comment
@CraigAnderson Решил ли это вашу проблему? Вы не приняли / не проголосовали за ответ, поэтому я понятия не имею, нужна ли вам дополнительная помощь. - person tpeczek; 30.04.2012
comment
Я столкнулся с той же проблемой, и предложение обновить теги legacyImpersonationPolicy и alwaysFlowImpersonationPolicy работают, но я бы хотел избежать этого, поскольку это изменение затрагивает всю машину. Эта ссылка указывает на то, что можно иметь файл aspnet.config для каждого пула приложений, но мне не удалось заставить его работать. Кому-нибудь повезло с этим? weblogs.asp.net/owscott/archive/2011/12/01/ - person Abhijeet Patel; 22.11.2013
comment
У вас есть пример или хороший ресурс для создания собственного HttpClientHandler, чтобы справиться с этой ситуацией, как вы рекомендовали? Спасибо. - person eaglei22; 06.07.2017
comment
Ссылка на эту статью теперь мертва. Возможно, вместо этого support.microsoft.com/en-us/help/926642 ? - person Caltor; 11.06.2020

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

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

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.

person Joshua    schedule 01.10.2012
comment
Спасибо, это то, что мне нужно. - person Kevin Stricker; 19.11.2014
comment
Ух ты. WindowsImpersonationContext. Вы спасли день и мое здравомыслие. Спасибо. - person granadaCoder; 06.08.2015
comment
Вы пробовали использовать аналоги async, такие как UploadDataAsync :-) - person Abhijeet Patel; 27.04.2017
comment
У меня это не сработало. Я все еще получаю 401 Неавторизованный. - person eaglei22; 05.07.2017

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

Первый прыжок - это веб-сервер, и для работы олицетворения с проверкой подлинности 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/ (Для выполнения этих операций вам потребуются достаточные права доступа.)

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

person Luis Cantero    schedule 01.03.2013

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

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

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

person Hardik Patel    schedule 11.07.2016