我正在使用基于WCF的客户端/服务器应用程序(WCF是自托管的,而不是在IIS中)。
WCF服务具有将一大块数据上载到服务器的操作。合同大致如下:
void UploadChunk(int clientId, byte[] chunk);
我们正在使用Windows身份验证(Kerberos / NTLM),因此我们无法在此处使用流式传输。
绑定看起来像这样(客户端和服务器端):
new BasicHttpBinding
{
Security = new BasicHttpSecurity
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = { ClientCredentialType = HttpClientCredentialType.Windows },
},
MaxReceivedMessageSize = 0x7fffffff,
ReaderQuotas = { MaxArrayLength = 0x800000 },
};
客户端通过从System.ServiceModel.ClientBase<TChannel>
派生的代理对象与服务进行通信。
所有这一切都很好,但我们观察到WCF客户端每次发送两次HTTP请求,一次没有auth头,再一次使用正确的auth头。这是有问题的,因为请求将非常大,并且此行为导致请求大小是实际块大小的两倍。
我已经发现(https://weblog.west-wind.com/posts/2010/Feb/18/NET-WebRequestPreAuthenticate-not-quite-what-it-sounds-like)将WebRequest.PreAuthenticate
设置为true
会记住auth标头,并将其重新用于后续请求。
然而,从我目前看来,WCF没有公开修改WebRequest实例的机制。
这个问题有解决办法吗?
答案 0 :(得分:3)
对于Windows身份验证,您的第一个请求始终会有质询响应(401
)。
如果您掌控所有客户,我认为最实用的解决方案是以最小的负载执行操作。
操作void IsAuthenticated()
应该这样做。对于每个客户端代理实例,您可以在IsAuthenticated
之前调用UploadChunk
。
IsAuthenticated
请求可以让您通过401
质询响应,而无需发送大型有效负载,但会对连接进行身份验证。对该连接的后续请求不会受到质疑。
修改:
我所描述的行为似乎只适用于IIS 8.所以我仔细研究了两个http.sys跟踪,一个用于IIS托管服务,另一个用于自托管服务。
IIS托管服务似乎在身份验证方面采用了某种优化方式。第一个连接请求使用Authenticator Sspi Authenticator
进行身份验证。后续请求使用Fast Authenticator
进行身份验证。
这些事件都不存在于自主机跟踪中,这导致我得出的结论是自托管未针对Windows身份验证进行优化。
然后我发现this blog entry提出了使用 NTLM 的解决方案,一个自定义绑定和HTTP传输的unsafeConnectionNtlmAuthentication
设置。如果您愿意仅使用 NTLM 并且documentation中突出显示的安全问题不是问题,那么这似乎可以提供您正在寻找的行为根据http.sys跟踪。
http.sys trace - self host with custom binding
对于服务器使用绑定
<customBinding>
<binding name="myBinding">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport authenticationScheme="Ntlm" unsafeConnectionNtlmAuthentication="true"/>
</binding>
</customBinding>
对于您的客户端,您可以使用具有Ntlm安全性的常规basicHttpBinding:
<basicHttpBinding>
<binding name="BasicHttpBinding_ITest">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
答案 1 :(得分:0)
使用HttWebRequest调用WCF服务,手动创建完整的SOAP消息。这将允许将PreAuthenticate设置为true。首次调用没有有效负载,Authenticate()。然后用有效载荷发出请求。