単純なC#NET Framework 4.0アプリケーションを考えてみます。
正常に動作するサンプルは次のとおりです。
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認証が開始されます(サーバーは "WWW-Authenticate:NTLM"ヘッダーを返し、認証/承認プロセス全体が繰り返されます。 "Connection:close"ヘッダーはありません)。
NTLMは、要求ではなく接続を認証することになっているのではないですか?
各リクエストを再認証する必要をなくすために、WebClientに既存の接続を再利用させる方法はありますか?
10日間、考えられるすべてのことを試し、その過程で多くのことを学んだ後、私はついにこの問題の修正を見つけました。
コツは、UnsafeAuthenticatedConnectionSharing
を上書きし、GetWebRequest
でプロパティをtrue
に設定することで、 HttpWebRequest
を有効にすることです。
これを 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 に感謝します。彼の提案は私にとってはうまくいきませんでしたが、彼は私を正しい方向に向けて探し、最終的に答えを見つけました。
IISの設定を確認します。ただし、これがデフォルトであるはずです。
<windowsAuthentication
enabled="True"
authPersistSingleRequest="False"
UseKernelMode>
</windowsAuthentication>
参照: https://msdn.Microsoft.com/en-us/library/aa347472.aspx
Localhost IISが存在するゾーンを確認しましたか?これは、WinInetを使用するときに過去にクライアント側からの落とし穴でもありました。WebClient
のデフォルトの動作を確認してください。
編集:
エラーを再現した後、WebClient
のNTLMpreauthentication実装が不足しているため、単一の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;
}
}