asp.net - 凭据未在生产服务器中的 WCF 调用中传递

标签 asp.net wcf

场景

我们有一个 Web 应用程序,它使用以下代码调用 Web 服务(托管在同一台机器的 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();
}

代码的意图是使用 Web 应用程序用户的凭据执行 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

请注意,对 Web 应用程序的请求已记录我的用户名 - 但对服务的请求未记录。将这些日志与我的开发机器上的日志进行比较,我在两个请求中都看到了我的名字 - 所以我认为这是失败的原因。

这是 WCF 配置 - 尽管它在 Dev + QA 中工作的事实让我怀疑这没有错。任何想法/帮助将不胜感激。

Web 应用程序(即 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>

Web 服务应用程序的配置文件包含此
    <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)

质量保证:Win 2003 32 位 SP2 (IIS 6.0)

开发人员:各种!我自己的系统是 Win7 64 (IIS 7.5) - 我们在 Vista 上也有开发人员。

问题的原因和解决方法

像往常一样,魔鬼在细节中。为什么哦为什么它总是在细节中!包括血腥的细节,以防他们帮助其他有类似问题的人。

原因

为了使问题尽可能简单,我省略了 Web 应用程序和 Web 服务未托管在默认 IIS 网站中的情况。相反,它们被托管在一个单独的网站上(原因是我们在同一台服务器上托管了多个应用程序+同一个应用程序的版本),我们使用主机头信息来识别网站。

因此,生产站点(和网络服务)只能通过像“app.companyName.com”这样的 URL 访问。因此,当我说客户端 web.config 有 address="http://localhost/MyServicesApp/ReportFormatter.svc/ntlm" 时,我撒了谎。

它实际上有
address="http://app.companyName.com/MyServicesApp/ReportFormatter.svc/ntlm"
我现在觉得有点愚蠢,因为我知道我们无法跨机器边界传递 NTLM 身份验证 token - 就 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"

  • 到 qaServerName - 因此在逻辑上是“本地主机”(因为客户端和服务器都在同一台机器上)
    但是当主机名是
  • "qaServerNameDnsRecord2"
  • "qaServerNameDnsRecord2.domainName.local"

  • 我敢肯定,如果我对 DNS 或 Windows 如何解析名称有所了解,那么这不会让人感到意外。但我了解到,将来我不会那么快

    解决方案

    知道问题在于地址正在解析为看起来像是远程的地址,解决方案很简单 - 只需使地址看起来是本地的(如果 Web 服务托管在默认网站中,那将是 super 简单 - 只需将 address="http://localhost/..." 作为端点地址!)。

    我尝试的第一件事是编辑 QA 服务器上的主机文件,并添加一个将“app.companyName.com”映射到 127.0.0.1 的条目,但这不起作用。

    所以我做的下一件(相当不雅的)事情是为网站添加一个额外的身份,主机 header 值为 127.0.0.1然后将配置更改为 address="http://127.0.0.1/..." (我想我可以使用 localhost 而不是 127.0.0.1)。这样就解决了所有问题。

    最佳答案

    我想在生产中 WCF 和网络服务器在不同的机器上运行,而在 DEV 和 QA 中它们不是?

    您需要通过 Kerberos 身份验证委派用户身份才能模拟 WCF。原因:WCF 不知道用户凭据,因此无法正确模拟。在 Kerberos 方案中,将使用身份验证 token 而不是真正的凭据。

    如果您需要一个起点来进入 Kerberos 主题,请搜索“Kerberos IIS 委托(delegate) 2003 Windows 身份验证”或类似内容。

    关于asp.net - 凭据未在生产服务器中的 WCF 调用中传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4680943/

    相关文章:

    c# - Swift:这个 nil 是如何进入我的 [String: AnyObject] 字典的,我该如何摆脱它?

    c# - 基于端点的路由 .net core 3.1 中缺少端点

    asp.net - 如何使用客户端函数 "OnClientClicking"传递绑定(bind)或评估参数

    c# - 在 MVC 4 中使用 Dapper 的存储过程

    WCF单元测试

    c# - 如何在基于 WCF 的场景中管理 EF 4.2 关联

    c# - Aspx 是否支持 PWA

    html - 下拉列表箭头图标在 IE 上无法正常工作

    c# - 为 PerSession WCF 服务调用两次静态构造函数

    ios - iOS 应用程序可以使用 wsHttpBinding 使用 WCF 服务吗?