Considérons une simple application C # NET Framework 4.0, qui:
Voici un exemple qui fonctionne bien:
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();
}
}
}
Le problème:
Lors de l'activation du traçage, je constate que l'authentification NTLM ne persiste pas.
Chaque fois que Webclient.DownloadString est appelé, l'authentification NTLM démarre (le serveur renvoie l'en-tête "WWW-Authenticate: NTLM" et l'ensemble du processus d'authentification/autorisation se répète; il n'y a pas d'en-tête "Connection: close").
NTLM n'était-il pas censé authentifier une connexion, pas une demande?
Existe-t-il un moyen de faire en sorte que WebClient réutilise une connexion existante pour éviter d'avoir à ré-authentifier chaque demande?
Après 10 jours à essayer tout ce à quoi je pouvais penser et à apprendre beaucoup dans le processus, j'ai finalement trouvé une solution à ce problème.
L'astuce consiste à activer UnsafeAuthenticatedConnectionSharing
en remplaçant GetWebRequest
et en définissant la propriété sur true
dans le HttpWebRequest
que vous récupérez.
Vous souhaiterez peut-être combiner cela avec la propriété ConnectionGroupName
pour éviter l'utilisation potentielle de la connexion par des applications non authentifiées.
Voici l'exemple de la question modifiée pour fonctionner comme prévu. Il ouvre une seule connexion authentifiée NTLM et la réutilise:
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;
}
}
}
À ce stade, je voudrais également remercier @ Falco Alexander pour toute l'aide; alors que ses suggestions ne fonctionnaient pas tout à fait pour moi, il m'a indiqué la bonne direction à rechercher et a finalement trouvé la réponse.
Vérifiez le paramètre de votre IIS, bien que cela devrait être par défaut.
<windowsAuthentication
enabled="True"
authPersistSingleRequest="False"
UseKernelMode>
</windowsAuthentication>
Réf: https://msdn.Microsoft.com/en-us/library/aa347472.aspx
Avez-vous vérifié la zone dans laquelle se trouve votre hôte local IIS? C'était également un piège du côté client dans le passé lorsque vous travailliez avec WinInet. Vérifiez le comportement par défaut de WebClient
.
Éditer:
Après avoir reproduit l'erreur, je pourrais comprendre que c'est l'implémentation NTLM de préauthentification de WebClient
qui vous empêche d'une seule requête 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;
}
}