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

Flo*_*her 11 c# wcf windows-authentication

我正在研究基于WCF的客户端/服务器应用程序(WCF是自托管的,而不是在IIS中).

WCF服务具有将一大块数据上载到服务器的操作.合约大致如下:

void UploadChunk(int clientId, byte[] chunk);
Run Code Online (Sandbox Code Playgroud)

我们正在使用Windows身份验证(Kerberos/NTLM),因此我们无法在此处使用流式传输.

绑定看起来像这样(客户端和服务器端):

new BasicHttpBinding
{   
   Security = new BasicHttpSecurity
   {
      Mode = BasicHttpSecurityMode.TransportCredentialOnly,
      Transport = { ClientCredentialType = HttpClientCredentialType.Windows },
   },
   MaxReceivedMessageSize = 0x7fffffff,
   ReaderQuotas = { MaxArrayLength = 0x800000 },
};
Run Code Online (Sandbox Code Playgroud)

客户端通过派生自的代理对象与服务进行通信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.PreAuthenticatetrue记住auth标头并重新使用它适用于后续请求.

但是从我看到的到现在为止,WCF没有公开修改WebRequest实例的机制.

这个问题有什么解决方案吗?

Ami*_*ain 3

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

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

操作void IsAuthenticated()应该可以。对于每个客户端代理实例,您将调用IsAuthenticatedbefore UploadChunk

IsAuthenticated请求将使您401在不发送大负载的情况下克服质询响应,但会对连接进行身份验证。对该连接的后续请求将不会受到质疑。

编辑 :

我描述的行为似乎只适用于 IIS 8。因此,我仔细查看了两个 http.sys 跟踪,一个用于 IIS 托管服务,一个用于自托管服务。

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

自托管跟踪中不存在这些事件,这使我得出结论:自托管未针对 Windows 身份验证进行优化。

http.sys - 跟踪 IIS

http.sys - 跟踪自身主机

然后我发现这篇博客文章提出了使用NTLM、自定义绑定和unsafeConnectionNtlmAuthenticationHTTP 传输设置的解决方案。如果您愿意只使用NTLM并且文档中突出显示的安全问题不是问题,那么这似乎提供了您根据 http.sys 跟踪正在寻找的行为。

http.sys 跟踪 - 具有自定义绑定的自主机

对于服务器使用绑定

<customBinding>
    <binding name="myBinding">
      <textMessageEncoding messageVersion="Soap11" />
      <httpTransport authenticationScheme="Ntlm" unsafeConnectionNtlmAuthentication="true"/>
    </binding>
  </customBinding>
Run Code Online (Sandbox Code Playgroud)

对于您的客户端,您可以使用具有 Ntlm 安全性的常规 basicHttpBinding:

<basicHttpBinding>
    <binding name="BasicHttpBinding_ITest">
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Ntlm" />
      </security>
    </binding>
  </basicHttpBinding>
Run Code Online (Sandbox Code Playgroud)