为每个请求启动 C# WebClient NTLM 身份验证
C# WebClient NTLM authentication starting for each request
考虑一个简单的 C# NET Framework 4.0 应用程序,即:
- 使用网络客户端
- 使用 NTLM 进行身份验证(在 IIS 6.0 和 IIS 7.5 服务器上测试)
- 使用 DownloadString()
多次从 URL 中检索字符串
这是一个运行良好的示例:
using System;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string URL_status = "http://localhost/status";
CredentialCache myCache = new CredentialCache();
myCache.Add(new Uri(URL_status), "NTLM", new NetworkCredential("username", "password", "domain"));
WebClient WebClient = new WebClient();
WebClient.Credentials = myCache;
for (int i = 1; i <= 5; i++)
{
string Result = WebClient.DownloadString(new Uri(URL_status));
Console.WriteLine("Try " + i.ToString() + ": " + Result);
}
Console.Write("Done");
Console.ReadKey();
}
}
}
问题:
启用跟踪时,我发现 NTLM 身份验证没有持续存在。
每次调用 Webclient.DownloadString 时,NTLM 身份验证开始(服务器 returns "WWW-Authenticate: NTLM" header 并且整个 authenticate/authorize 过程重复;没有 "Connection: close" header).
NTLM 不是应该验证连接,而不是请求吗?
有没有办法让 WebClient 重用现有连接以避免必须 re-authenticate 每个请求?
检查您的 IIS 设置,虽然这应该是默认设置。
<windowsAuthentication
enabled="True"
authPersistSingleRequest="False"
UseKernelMode>
</windowsAuthentication>
参考:https://msdn.microsoft.com/en-us/library/aa347472.aspx
您是否检查过您的本地主机 IIS 所在的区域?这也是以往客户端在使用 WinInet 时的一个陷阱。检查 WebClient
.
的默认行为
编辑:
重现错误后,我发现是 WebClient
缺少的 NTLM preauthentication 实现使您无法收到单个 401 请求:
var WebClient = new PreAuthWebClient();
WebClient.Credentials = new NetworkCredential("user", "pass","domain");
//Do your GETs
Public class PreAuthWebClient: WebClient
{
protected override WebRequest GetWebRequest (Uri address)
{
WebRequest request = (WebRequest) base.GetWebRequest (address);
request.PreAuthenticate = true;
return request;
}
}
在尝试了 10 天我能想到的一切并在此过程中学到了很多东西之后,我终于找到了解决此问题的方法。
诀窍是通过覆盖 GetWebRequest
并在 HttpWebRequest
中将 属性 设置为 true
来启用 UnsafeAuthenticatedConnectionSharing
。
您可能希望将其与 ConnectionGroupName
属性 结合使用,以避免未经身份验证的应用程序可能使用该连接。
这里是问题中的示例,已修改为按预期工作。它打开单个 NTLM 身份验证连接并重新使用它:
using System;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string URL_status = "http://localhost/status";
CredentialCache myCache = new CredentialCache();
myCache.Add(new Uri(URL_status), "NTLM", new NetworkCredential("username", "password", "domain"));
MyWebClient webClient = new MyWebClient();
webClient.Credentials = myCache;
for (int i = 1; i <= 5; i++)
{
string result = webClient.DownloadString(new Uri(URL_status));
Console.WriteLine("Try {0}: {1}", i, result);
}
Console.Write("Done");
Console.ReadKey();
}
}
public class MyWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
var myWebRequest = request as HttpWebRequest;
myWebRequest.UnsafeAuthenticatedConnectionSharing = true;
myWebRequest.KeepAlive = true;
}
return request;
}
}
}
此时我还要感谢@Falco Alexander的所有帮助;虽然他的建议对我来说不太奏效,但他确实为我指明了寻找并最终找到答案的正确方向。
考虑一个简单的 C# NET Framework 4.0 应用程序,即:
- 使用网络客户端
- 使用 NTLM 进行身份验证(在 IIS 6.0 和 IIS 7.5 服务器上测试)
- 使用 DownloadString() 多次从 URL 中检索字符串
这是一个运行良好的示例:
using System;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string URL_status = "http://localhost/status";
CredentialCache myCache = new CredentialCache();
myCache.Add(new Uri(URL_status), "NTLM", new NetworkCredential("username", "password", "domain"));
WebClient WebClient = new WebClient();
WebClient.Credentials = myCache;
for (int i = 1; i <= 5; i++)
{
string Result = WebClient.DownloadString(new Uri(URL_status));
Console.WriteLine("Try " + i.ToString() + ": " + Result);
}
Console.Write("Done");
Console.ReadKey();
}
}
}
问题:
启用跟踪时,我发现 NTLM 身份验证没有持续存在。
每次调用 Webclient.DownloadString 时,NTLM 身份验证开始(服务器 returns "WWW-Authenticate: NTLM" header 并且整个 authenticate/authorize 过程重复;没有 "Connection: close" header).
NTLM 不是应该验证连接,而不是请求吗?
有没有办法让 WebClient 重用现有连接以避免必须 re-authenticate 每个请求?
检查您的 IIS 设置,虽然这应该是默认设置。
<windowsAuthentication
enabled="True"
authPersistSingleRequest="False"
UseKernelMode>
</windowsAuthentication>
参考:https://msdn.microsoft.com/en-us/library/aa347472.aspx
您是否检查过您的本地主机 IIS 所在的区域?这也是以往客户端在使用 WinInet 时的一个陷阱。检查 WebClient
.
编辑:
重现错误后,我发现是 WebClient
缺少的 NTLM preauthentication 实现使您无法收到单个 401 请求:
var WebClient = new PreAuthWebClient();
WebClient.Credentials = new NetworkCredential("user", "pass","domain");
//Do your GETs
Public class PreAuthWebClient: WebClient
{
protected override WebRequest GetWebRequest (Uri address)
{
WebRequest request = (WebRequest) base.GetWebRequest (address);
request.PreAuthenticate = true;
return request;
}
}
在尝试了 10 天我能想到的一切并在此过程中学到了很多东西之后,我终于找到了解决此问题的方法。
诀窍是通过覆盖 GetWebRequest
并在 HttpWebRequest
中将 属性 设置为 true
来启用 UnsafeAuthenticatedConnectionSharing
。
您可能希望将其与 ConnectionGroupName
属性 结合使用,以避免未经身份验证的应用程序可能使用该连接。
这里是问题中的示例,已修改为按预期工作。它打开单个 NTLM 身份验证连接并重新使用它:
using System;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string URL_status = "http://localhost/status";
CredentialCache myCache = new CredentialCache();
myCache.Add(new Uri(URL_status), "NTLM", new NetworkCredential("username", "password", "domain"));
MyWebClient webClient = new MyWebClient();
webClient.Credentials = myCache;
for (int i = 1; i <= 5; i++)
{
string result = webClient.DownloadString(new Uri(URL_status));
Console.WriteLine("Try {0}: {1}", i, result);
}
Console.Write("Done");
Console.ReadKey();
}
}
public class MyWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
var myWebRequest = request as HttpWebRequest;
myWebRequest.UnsafeAuthenticatedConnectionSharing = true;
myWebRequest.KeepAlive = true;
}
return request;
}
}
}
此时我还要感谢@Falco Alexander的所有帮助;虽然他的建议对我来说不太奏效,但他确实为我指明了寻找并最终找到答案的正确方向。