如何使用Windows身份验证防止重复的HTTP请求

时间:2016-03-23 17:54:49

标签: c# wcf windows-authentication

我正在使用基于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实例的机制。

这个问题有解决办法吗?

2 个答案:

答案 0 :(得分:3)

对于Windows身份验证,您的第一个请求始终会有质询响应(401)。

如果您掌控所有客户,我认为最实用的解决方案是以最小的负载执行操作。

操作void IsAuthenticated()应该这样做。对于每个客户端代理实例,您可以在IsAuthenticated之前调用UploadChunk

IsAuthenticated请求可以让您通过401质询响应,而无需发送大型有效负载,但会对连接进行身份验证。对该连接的后续请求不会受到质疑。

修改:

我所描述的行为似乎只适用于IIS 8.所以我仔细研究了两个http.sys跟踪,一个用于IIS托管服务,另一个用于自托管服务。

IIS托管服务似乎在身份验证方面采用了某种优化方式。第一个连接请求使用Authenticator Sspi Authenticator进行身份验证。后续请求使用Fast Authenticator进行身份验证。

这些事件都不存在于自主机跟踪中,这导致我得出的结论是自托管未针对Windows身份验证进行优化。

http.sys - trace IIS

http.sys - trace self host

然后我发现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()。然后用有效载荷发出请求。