场景
我们有一个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();
}
Run Code Online (Sandbox Code Playgroud)
代码的意图是使用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
Run Code Online (Sandbox Code Playgroud)
请注意,对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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
OS详细信息
制作:Win 2003 32bit SP2(IIS 6.0)
QA:Win 2003 32位SP2(IIS 6.0)
开发:各种各样!我自己的系统是Win7 64(IIS 7.5) - 我们在Vista上也有开发人员.
像往常一样,魔鬼在细节.为什么哦为什么总是在细节!如果他们帮助其他有类似问题的人,则包括血腥细节.
为了使问题尽可能简单,我没有提到Web应用程序和Web服务未托管在默认的IIS网站中.相反,它们托管在一个单独的网站上(原因是我们在同一台服务器上托管了几个应用程序.+同一个应用程序的版本),我们使用主机头信息来识别网站.
因此,只能通过"app.companyName.com"之类的URL访问生产站点(和Web服务).因此,当我说客户端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"相同的处理.我甚至设置了"dnsAliasForTheQAServer" 的DNS 别名(在我们的本地域上),然后测试了以下工作
address="http://dnsAliasForTheQAServer.domainName.local/MyServicesApp/ReportFormatter.svc/ntlm"
它确实做到了!现在你必须承认,如果机器认为它被称为"qaServerName",那么你会认为"dnsAliasForTheQAServer.domainName.local"将被视为远程地址.所以我推断这可能不是导致生产服务器失败的问题.
只有当绝望导致我们实际创建一个新的DNS记录("qaServerNameDnsRecord2"),我们设法重现QA机器上的错误!因此,我得出结论,在WCF客户端和/或DNS上进行了一些非常聪明的名称解析,解析了以下主机名
到qaServerName - 因此逻辑上是"Localhost"(因为客户端和服务器都在同一台机器上)但是当主机名是时,名称解析并不聪明
我相信如果我知道关于DNS的事情或者Windows如何解析名称,那么这不会是一个惊喜.但我了解到,将来我不会那么快
知道问题是地址解析为一个看起来像是远程的地址,解决方案很简单 - 只需使地址看起来是本地的(如果Web服务托管在默认的超级网站中)简单 - 只需将其address="http://localhost/..."作为端点地址!).
我尝试的第一件事是编辑QA服务器上的hosts文件,并添加一个将'app.companyName.com'映射到127.0.0.1的条目,但这不起作用.
所以我做的下一个(相当不优雅)的事情是为主机标头值为网站添加额外的标识127.0.0.1,然后将配置更改为读取address="http://127.0.0.1/..."(我想我可以使用localhost而不是127.0.0.1).这就把一切都排好了.
我想在生产中 WCF 和 Web 服务器运行在不同的机器上,而在 DEV 和 QA 中则不是?
您需要通过 Kerberos 身份验证委派用户身份才能模拟 WCF。原因:WCF 不知道用户凭据,因此无法正确模拟。在 Kerberos 场景中,将使用身份验证令牌而不是真实凭据。
如果您需要一个起点来了解 Kerberos 主题,请在 google 中搜索“Kerberos IIS Delegation 2003 windows authentication”或类似内容。
| 归档时间: |
|
| 查看次数: |
6799 次 |
| 最近记录: |