2010-07-03 4 views
17

Verschiedene Artikel (1, 2) entdeckte ich diese einfach genug aussehen:Wie kann ich die Authentifizierung mit HttpWebRequest verdauen?

WebRequest request = HttpWebRequest.Create(url); 

var credentialCache = new CredentialCache(); 
credentialCache.Add(
    new Uri(url), // request url 
    "Digest", // authentication type 
    new NetworkCredential("user", "password") // credentials 
); 

request.Credentials = credentialCache; 

Allerdings funktioniert dies nur für URLs ohne URL-Parameter. Zum Beispiel kann ich http://example.com/test/xyz.html ganz gut herunterladen, aber wenn ich versuche, http://example.com/test?page=xyz herunterladen, ist das Ergebnis eine 400 Bad Request-Nachricht mit dem folgenden in den Protokollen des Servers (mit Apache 2.2):

Digest: uri mismatch - </test> does not match request-uri </test?page=xyz> 

Meine erste Idee war dass die Digest-Spezifikation erfordert, dass URL-Parameter aus dem Digest-Hash entfernt werden - aber das Entfernen des Parameters aus der URL, die an credentialCache.Add() übergeben wurde, hat nichts geändert. Es muss also umgekehrt sein und irgendwo im .NET-Framework wird der Parameter fälschlicherweise aus der URL entfernt.

+0

Hier ist eine ähnliche Frage auf SO meine ursprüngliche Suche nicht mit: http://StackOverflow.com/Questions/3109507/httpwebrequests-Sends-Parameterless-URI-in-Autorization-Header – Cygon

+0

Und ein Microsoft Connect Bug-Report: https://connect.microsoft.com/VisualStudio/feedback/details/571052/digest-authentication-does-not-send-the-full-uri-path-in-the-uri-parameter – Cygon

+0

Die Microsoft Connect Fehlerbericht, der oben verlinkt ist, scheint eine Abhilfe zu haben, gepostet 6/26. Hast du das probiert? –

Antwort

0

Ich denke, die zweite URL verweist auf dynamische Seite und Sie sollten es zuerst mit GET aufrufen, um den HTML-Code zu erhalten und ihn dann herunterzuladen. Keine Erfahrung in diesem Bereich obwohl.

+0

Entschuldige Nein. Es hängt vollständig vom Webserver ab, was mit der URL zu tun ist und die erste Seite könnte ebenfalls dynamisch sein. Außerdem ist der HTML-Code der Download, es gibt keinen Unterschied zwischen dem Herunterladen von HTML oder dem Herunterladen von etwas anderem. – Cygon

4

Sie haben gesagt, dass Sie die Querystring-Parameter entfernt haben, aber haben Sie versucht, den ganzen Weg zurück zum Host zu gehen? Jedes einzelne Beispiel von CredentialsCache.Add(), das ich gesehen habe, scheint nur den Host zu verwenden, und die Dokumente für CredentialsCache.Add() listen den Uri-Parameter als "uriPrefix" auf, was aussagekräftig erscheint.

Mit anderen Worten, versuchen Sie dies aus:

Uri uri = new Uri(url); 
WebRequest request = WebRequest.Create(uri); 

var credentialCache = new CredentialCache(); 
credentialCache.Add( 
    new Uri(uri.GetLeftPart(UriPartial.Authority)), // request url's host 
    "Digest", // authentication type 
    new NetworkCredential("user", "password") // credentials 
); 

request.Credentials = credentialCache; 

Wenn das funktioniert, werden Sie auch sicher haben, dass Sie die gleiche „Autorität“ nicht mehr als einmal in den Cache hinzufügen ... Alle Anfragen an denselben Host sollten den gleichen Credential-Cache-Eintrag verwenden können.

+0

Seltsam, ich habe kein einziges Beispiel mit nur der Root-URI für die Authentifizierung gefunden. Auf jeden Fall funktioniert es nicht, sorry. Gemäß Abschnitt 3.2.2 von RFC 2617 (http://rfc.askapache.com/rfc2617/rfc2617.html#section-3.2.2) sollte der Digest-URI identisch mit dem 'request-uri' in der HTTP-Anfrage sein. – Cygon

+0

Hier sind einige Beispiele: http://msdn.microsoft.com/en-us/library/system.net.credentialcache.aspx, http://support.microsoft.com/kb/822456, http: // blogs. msdn.com/b/buckh/archive/2004/07/28/199706.aspx (obwohl es zugegebenermaßen ein "localhost" -Beispiel ist). – JaredReisinger

+0

Ja, die RFC besagt, dass die Digest-URI die Anfrage entsprechen sollte, aber das ist, was auf dem Draht gesendet wird, nicht was im Cache gespeichert ist. Die CredentialCache.GetCredential() doc (http://msdn.microsoft.com/en-us/library/fy4394xd.aspx) sagt, dass „getCredential der längsten passenden Präfix-URI in dem Cache verwendet, der Satz von Anmeldeinformationen zu bestimmen, um zurückzukehren ein Autorisierungstyp. " Es zeigt dann, dass beim Übergeben einer Domäne die Anmeldeinformationen für * alle * Ressourcen unter dieser Domäne verwendet werden. – JaredReisinger

1

Die Lösung ist es, diesen Parameter in Apache zu aktivieren:

BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On 


Weitere Informationen: http://httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie

Dann diese Eigenschaft in Ihrem Code hinzufügen, für das webrequest Objekt:

request.UserAgent = "MSIE" 

es für mich sehr gut funktionieren

+0

Ja, meine eigenen Kommentar auf die ursprüngliche Frage von Juli sehen, 21, 2010. Es ist nur eine Option, wenn Sie die Kontrolle über den Server haben, und es ärgert mich ein wenig, dass meine App selbst als MSIE zu identifizieren hat, obwohl;) – Cygon

2

-Code von diesem Posten genommen hat für mich gearbeitet perfekt Implement Digest authentication via HttpWebRequest in C#

I Ausgabe verfolgt hatte, wann immer ich Browser der Feed-URL in einem Browser für Benutzername und Passwort gefragt und funktionierte gut, jedoch funktionierte irgendeines der oben genannten Codebeispiele nicht, beim Überprüfen des Request/Response-Headers (in den Webentwicklertools in Firefox) konnte ich sehen, dass Header eine Autorisierung von Typ Digest hatte.

Schritt 1 Anzahl:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 
using System.Text.RegularExpressions; 
using System.Net; 
using System.IO; 

namespace NUI 
{ 
    public class DigestAuthFixer 
    { 
     private static string _host; 
     private static string _user; 
     private static string _password; 
     private static string _realm; 
     private static string _nonce; 
     private static string _qop; 
     private static string _cnonce; 
     private static DateTime _cnonceDate; 
     private static int _nc; 

    public DigestAuthFixer(string host, string user, string password) 
    { 
     // TODO: Complete member initialization 
     _host = host; 
     _user = user; 
     _password = password; 
    } 

    private string CalculateMd5Hash(
     string input) 
    { 
     var inputBytes = Encoding.ASCII.GetBytes(input); 
     var hash = MD5.Create().ComputeHash(inputBytes); 
     var sb = new StringBuilder(); 
     foreach (var b in hash) 
      sb.Append(b.ToString("x2")); 
     return sb.ToString(); 
    } 

    private string GrabHeaderVar(
     string varName, 
     string header) 
    { 
     var regHeader = new Regex(string.Format(@"{0}=""([^""]*)""", varName)); 
     var matchHeader = regHeader.Match(header); 
     if (matchHeader.Success) 
      return matchHeader.Groups[1].Value; 
     throw new ApplicationException(string.Format("Header {0} not found", varName)); 
    } 

    private string GetDigestHeader(
     string dir) 
    { 
     _nc = _nc + 1; 

     var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", _user, _realm, _password)); 
     var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", "GET", dir)); 
     var digestResponse = 
      CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, _nonce, _nc, _cnonce, _qop, ha2)); 

     return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", " + 
      "algorithm=MD5, response=\"{4}\", qop={5}, nc={6:00000000}, cnonce=\"{7}\"", 
      _user, _realm, _nonce, dir, digestResponse, _qop, _nc, _cnonce); 
    } 

    public string GrabResponse(
     string dir) 
    { 
     var url = _host + dir; 
     var uri = new Uri(url); 

     var request = (HttpWebRequest)WebRequest.Create(uri); 

     // If we've got a recent Auth header, re-use it! 
     if (!string.IsNullOrEmpty(_cnonce) && 
      DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0) 
     { 
      request.Headers.Add("Authorization", GetDigestHeader(dir)); 
     } 

     HttpWebResponse response; 
     try 
     { 
      response = (HttpWebResponse)request.GetResponse(); 
     } 
     catch (WebException ex) 
     { 
      // Try to fix a 401 exception by adding a Authorization header 
      if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized) 
       throw; 

      var wwwAuthenticateHeader = ex.Response.Headers["WWW-Authenticate"]; 
      _realm = GrabHeaderVar("realm", wwwAuthenticateHeader); 
      _nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader); 
      _qop = GrabHeaderVar("qop", wwwAuthenticateHeader); 

      _nc = 0; 
      _cnonce = new Random().Next(123400, 9999999).ToString(); 
      _cnonceDate = DateTime.Now; 

      var request2 = (HttpWebRequest)WebRequest.Create(uri); 
      request2.Headers.Add("Authorization", GetDigestHeader(dir)); 
      response = (HttpWebResponse)request2.GetResponse(); 
     } 
     var reader = new StreamReader(response.GetResponseStream()); 
     return reader.ReadToEnd(); 
    } 
} 

}

Schritt 2: Rufen Sie neue Methode

DigestAuthFixer digest = new DigestAuthFixer(domain, username, password); 
string strReturn = digest.GrabResponse(dir); 

wenn URL ist: http://xyz.rss.com/folder/rss dann domain: http://xyz.rss.com (Domain-Teil) dir :/Ordner/rss (Rest der URL)

Sie könnten es auch als Stream zurückgeben und XmlDocument Load() -Methode verwenden.

+0

Großer Artikel. Ich habe eine Frage, die ich bekomme var wwwAuthenticateHeader = ex.Response.Headers ["WWW-Authenticate"]; als null, was kann der Grund sein? –

Verwandte Themen