在 .NET 中的 HttpClient 上支持 Windows 认证

155 阅读3分钟

最近开发中遇到一个问题,需要通过 HttpClient 来访问一个受到 Windows 认证保护的站点,具体来说是就是 NTLM 认证。在曾经的基于微软技术的解决方案中,这是一个常见的保护方式,在 .NET Framework 中,相关的类型对此也提供了很方便的支持。不过,在现代的 .NET 环境下,又怎样支持 Windows 认证呢?

在 .NET 中的 HttpClient 上支持 Windows 验证

对于整个 .NET Framework 技术栈来说,其中的 WebClient 和 HttpWebRequest 特别构建在 Windwos 之上,因此在这些 Web 客户端本身都内置支持 Windows 验证,如何设置 Windows 凭据,并在每次请求中传递它们都显而易见。

对于 .NET 版本的现代跨平台的 HttpClient 来说,基于 NTLM 的安全性和使用自动处理的凭据则不是那么普遍了。导致使用 Windows 验证安全性的方法不是那么容易发现。在 .NET 的文档中,除了 Basic 和 Digest 之外的方式被忽略了,尽管 Windows 验证机制实际上需要 Negotiate 和 NTLM 方式。

不需要唠唠叨叨,下面就是针对 .NET 版本的 HttpClient 使用 Windows 验证的代码:

// NTLM Secured URL 
var uri = new Uri("https://localhost:5200/someurl");
// Create a new Credential - note NTLM is not documented but works 
var credentialsCache = new CredentialCache();
credentialsCache.Add( uri, "NTLM", new NetworkCredential("userid", "******")); 
var handler = new HttpClientHandler() { 
    Credentials = credentialsCache, 
    PreAuthenticate = true 
}; 

var httpClient = new HttpClient(handler) { 
   Timeout = new TimeSpan(0, 0, 10) 
}; 
var response = await httpClient.GetAsync(uri); 
var result = await response.Content.ReadAsStringAsync(); Console.WriteLine(result);

解决这个问题的关键点是 CredentialCache,它的内部是一个 NetworkCredential 对象的集合,在这里你可以将针对 Windows 验证的 Negotiate 或者 NTLM 类型添加进来。尽管它们没有在文档中声明。NetworkCredential 对象可以处理典型的基于用户名和口令的凭据,例如对于 Widnows 验证,或者 Basic/Digest。

对于 NetworkCredential 来说,你需要提供 base URL 或者完全的 URL 和验证类型来应用它。

对于 base URL 来说,通常你需要提供一个 base URL,比如说 somesite.dom/ 而不是上面示例中的完全的 URL,因为 HttpClient 可能需要针对不同的 URL 请求共享同一个实例。

支持的验证类型包括:

  • Basic
  • Digest
  • NTLM
  • Negotiate
  • Kerberos

需要注意的是,这里的 HttpClient 与以前的 WebClient 和 HttpWebRequest 不同,它不会自动 PreAuthenticate 验证请求,这意味着它需要在正式发送数据之前需要 被 Challenge,即使你已经在 Credential Cache 中提供了凭据。对于客户端来说,这意味着对于每个发送到服务器端的没有提供凭据的请求,会得到一个 401 的 Challenge,然后再重新发送验证头,这样导致了额外的流量。对于大多数的客户端应用来说,你大概率希望设置 PreAuthenticate = true 来强制 HttpClient 立即发送验证信息,而不是在收到来自服务器的 401 Challenge 之后。

这些代码足够简单而且可以工作,但是由于关于 Windows 验证的文档的缺失,却不是那么显而易见的可以找到。