Getting HttpClient to work with Kerberos

2019-07-14 23:56发布

问题:

I'm building an application in an environment that's configured with Kerberos authentication. My application uses .NET's HttpClient to POST some data to an API hosted in IIS on a different server. GET requests seem to be working fine, but POSTs to the same service instantly implode with the following exception:

System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.Net.WebException: The request was aborted: The request was canceled. at System.Net.ConnectStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state) at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) --- End of inner exception stack trace --- at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) at System.Net.Http.StreamToStreamCopy.StartRead()

I tried looking at the requests using Fiddler, which as I understand it injects a HTTP proxy that it uses to view HTTP requests. And when using Fiddler, my POST request succeeds.

Here's what I see:

  • Request 1 results in a 401 Unauthorized. When I inspect the request headers, there's no Authorization. The response headers contain WWW-Authenticate: Negotiate and WWW-Authenticate: NTLM.
  • Request 2 also results in a 401 Unauthorized. This time, it sends a Authorization: Negotiate YIGeBgYrBgEFBGQKgg (...) header with the request. The response headers now comes back with a WWW-Authenticate: Negotiate oRUwE6A (...).
  • Request 3 again results in a 401 Unauthorized. It sends a new Authorization: Negotiate oTMwMaADCg (...) header with the request, and gets a new WWW-Authenticate: Negotiate oYIBCzCCAQe (...) header with the response.
  • Request 4 finally results in a 200 OK, with the expected response body. It also has a Authorization: Negotiate oYICRzCCAkOg (...) header in the request, and another WWW-Authenticate: Negotiate oRswGaADCg (...) in the response.

This appears to match the Kerberos message flow as I understand it - the first request is attempted anonymously, and gets a reply that authentication is needed. The second request asks for a ticket to the API server, which is then passed to the Kerberos controller, which again returns a ticket and a key for the client/server to use. The third request goes directly to the API server to verify that both of them can use the ticket, and the server confirms this. The fourth request uses the ticket and all is well.

However, using Fiddler as some ghetto-proxy on the server is not a viable option. So, my question:

How can I handle this in my code? My HttpClient has both PreAuthenticate and UseDefaultCredentials set to true, and WindowsIdentity.GetCurrent() returns my user with the correct domain name. However, it seems to do nothing. Without Fiddler I have no way of seeing the HTTP request, but I assume it just tries the first request and commits suicide once that fails. Do I have to apply some custom proxy to make this work? Or should it work out of the box?