Учетные данные не передаются при вызове WCF на рабочем сервере - PullRequest
5 голосов
/ 13 января 2011

Сценарий

У нас есть веб-приложение, которое выполняет вызов веб-службы (размещенной в IIS на той же машине), используя следующий код: -

using (HttpContext.Current.Request.LogonUserIdentity.Impersonate())
{
    var client = new Services.ReportFormatter(endpointName);  // endpoint name is configured in config
                                                       // Services.ReportFormatter is the generated client code using svcutil
    var response = client.DoWork();
}

Целью кода является то, что вызов WCF выполняется с использованием учетных данных пользователя веб-приложения (я надеюсь, что это очевидно!), И код работает как прелесть на машинах Dev и QA.Излишне говорить, что он не работает в производственном процессе!

Проблема

Создается исключение .Net:

The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM,Basic realm="Services"'. InnerException:The remote server returned an error: (401) Unauthorized.

Интересно, что в журналах IIS предполагается, что учетные данные пользователя не передаются в запросе WCF

Fields: 
cs-method cs-uri-stem cs-uri-query              s-port cs-username c-ip sc-status sc-substatus sc-win32-status 
GET       /MyWebApp/MyPage.aspx                   - 443 chrisf x.x.x.x
POST      /MyServicesApp/ReportFormatter.svc/ntlm - 80  -            x.x.x.x 401 2 2148074254
POST      /MyServicesApp/ReportFormatter.svc/ntlm - 80  -            x.x.x.x 401 1 0
POST      /MyServicesApp/ReportFormatter.svc/ntlm - 80  -            x.x.x.x 401 1 2148074252

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

Вот конфигурация WCF - хотя тот факт, что она работает в Dev +QA заставляет меня подозревать, что это не вина.Буду признателен за любые идеи / помощь.

Конфигурация веб-приложения (т.е. клиента WCF).

<bindings>
  <basicHttpBinding>

    <binding name="ReportFormatter" closeTimeout="00:01:00" openTimeout="00:01:00"
        receiveTimeout="00:10:00" sendTimeout="00:05:00" allowCookies="false"
        bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
        maxBufferSize="6553600" maxBufferPoolSize="524288" maxReceivedMessageSize="6553600"
        messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
        useDefaultWebProxy="true" >
      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Windows" />
        <message clientCredentialType="UserName" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

<client>
  <endpoint binding="basicHttpBinding" bindingConfiguration="ReportFormatter" contract="Services.ReportFormatter"
    name="ReportFormatter_Endpoint" address="http://localhost/MyServicesApp/ReportFormatter.svc/ntlm"/>
</client>

Файл конфигурации приложения веб-службы содержитэто

    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpWindowsBindingWinCred" maxReceivedMessageSize="20000000">
                <readerQuotas maxStringContentLength="20000000" maxArrayLength="1000000" maxBytesPerRead="20000000" />
                <security mode="TransportCredentialOnly">
                    <!-- clientCredentialType Windows requires IIS to be configured to support Windows authentication-->
                    <transport clientCredentialType="Windows" />
                    <message clientCredentialType="UserName" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>## Heading ##
    <services>
      <service behaviorConfiguration="IIS.Service1Behavior" name="Services.FormatReportImpl">
          <endpoint address="ntlm" binding="basicHttpBinding" bindingConfiguration="BasicHttpWindowsBindingWinCred" name="FormatReportBindingWindowsAuthentication" contract="Services.ReportFormatter" bindingNamespace="http://www.donkey.com/Reporting" />
      </service>
    </services>

Сведения об ОС

Производство: Win 2003 32bit SP2 (IIS 6.0)

QA: Win 2003 32bit SP2 (IIS 6.0)

Dev: Разное!Моя собственная система - Win7 64 (IIS 7.5) - у нас также есть разработчики на Vista.

Причина проблемы и решение

Как обычно, дьявол кроется в деталях.О, это всегда в деталях!Gory детали включены в случае, если они помогают кому-то еще с аналогичными проблемами.

Причина

Чтобы сделать вопрос как можно более простым, я не упомянул, что веб-приложение и Интернетслужба не была размещена на веб-сайте IIS по умолчанию.Вместо этого они размещаются на отдельном веб-сайте (причина в том, что мы размещаем несколько приложений. + Версии одного и того же приложения. На одном сервере), и мы используем информацию заголовка узла для идентификации веб-сайта.

Таким образом, рабочий сайт (и веб-сервис) доступен только через URL-адрес, такой как «app.companyName.com».Таким образом, Я солгал , когда я сказал, что у клиента web.config было address="http://localhost/MyServicesApp/ReportFormatter.svc/ntlm"

На самом деле у него было

address="http://app.companyName.com/MyServicesApp/ReportFormatter.svc/ntlm"

Я чувствую себя немноготеперь это глупо, потому что я знал, что мы не можем передать маркер аутентификации NTLM через границу компьютера - и что касается клиента WCF, app.companyName.com - это удаленный компьютер;он не имеет ни малейшего представления, что DNS-карты, которые обращаются к одной и той же машине, - ну, это может не иметь идеи (продолжайте читать)

Извините

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

address="http://qaServerName.domainName.local/MyServicesApp/ReportFormatter.svc/ntlm"

Я логически подумал, что этот адрес должен идти в DNS домена, который должен быть разрешени поэтому логически будет проходить ту же обработку, что и «app.companyName.com».Я даже настроил DNS псевдоним «dnsAliasForTheQAServer» (в нашем локальном домене) и затем проверил, что следующее работает

address="http://dnsAliasForTheQAServer.domainName.local/MyServicesApp/ReportFormatter.svc/ntlm"

, и это сработало!Теперь вы должны признать, что если машина думает, что она называется «qaServerName», то вы думаете, что «dnsAliasForTheQAServer.domainName.local» будет обрабатываться как удаленный адрес.Поэтому я решил, что это не может быть проблемой, вызывающей сбой в производственном сервере.

Только когда отчаяние привело нас к созданию новой записи DNS («qaServerNameDnsRecord2»), нам удалось воспроизвести ошибкуна машине QA!Таким образом, я заключаю, что на клиенте WCF и / или DNS выполняется некоторое очень умное разрешение имен, которое разрешает следующие имена хостов

  • "qaServerName"
  • "qaServerName.domainName.local"
  • "dnsAliasForTheQAServer.domainName.local"

to qaServerName - и, следовательно, логически «Localhost» (так как и клиент, и сервер находятся на одной машине) но разрешение имени не такое умное, когда имя хоста

  • "qaServerNameDnsRecord2"
  • "qaServerNameDnsRecord2.domainName.local"

Я уверен, что если бы я знал кое-что о DNS или о том, как Windows разрешает имена, то это не было бы сюрпризом. Но я узнал, что в будущем я не буду так быстро, чтобы

Решение

Вооружившись знанием о том, что проблема заключалась в том, что адрес преобразовывался в адрес, который выглядел как удаленный, решение было простым - просто сделать адрес локальным (если веб-служба размещалась на веб-сайте по умолчанию, который было бы очень просто - просто введите address="http://localhost/..." в качестве адреса конечной точки!).

Первым делом я попытался отредактировать файл hosts на сервере QA и добавить запись, которая сопоставила app.companyName.com с 127.0.0.1, но это не сработало.

Итак, следующее (довольно не элегантное) действие, которое я сделал, - это добавить дополнительную идентификацию для веб-сайта со значением заголовка хоста 127.0.0.1, а затем изменить конфигурацию на address="http://127.0.0.1/..." (полагаю, я мог бы использовать localhost вместо 127.0.0.1). И это все разобралось.

1 Ответ

2 голосов
/ 13 января 2011

Полагаю, в производстве WCF и веб-сервер работают на разных машинах, а в DEV и QA - нет?

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

Если вам нужна отправная точка для входа в тему Kerberos, поищите в Google «Проверка подлинности Windows Kerberos IIS Delegation 2003» или подобное

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...