最近开发中遇到一个问题,需要通过 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 验证的文档的缺失,却不是那么显而易见的可以找到。