Comment puis-je faire l'authentification digest avec HttpWebRequest?

divers articles (1, 2), j'ai découvert le rendre assez facile:

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;

cependant, cela ne fonctionne que pour les URL sans paramètres D'URL. Par exemple, je peux télécharger http://example.com/test/xyz.html très bien, mais lorsque je tente de télécharger http://example.com/test?page=xyz, le résultat est un message 400 Bad Request avec ce qui suit dans les logs du serveur (lancer Apache 2.2):

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

ma première idée était que la spécification digest exige que les paramètres D'URL soient supprimés du hachage digest -- mais en supprimant le paramètre de L'URL passée à credentialCache.Add() n'a rien changé. Donc ce doit être l'inverse et quelque part dans le framework .NET enlève à tort le paramètre de l'URL.

18
demandé sur Cygon 2010-07-03 22:54:00

4 réponses

Vous avez dit que vous avez enlevé les paramètres querystring, mais avez-vous essayé d'aller tout le chemin de retour à juste l'hôte? Chaque exemple de CredentialsCache.Ajouter() j'ai vu semble utiliser uniquement de l'accueil, et la doc de CredentialsCache.Ajouter() listez le paramètre Uri comme "uriPrefix", ce qui semble révélateur.

En d'autres termes, essayez ceci:

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;

Si cela fonctionne, vous devrez également vous assurer que vous n'ajoutez pas la même "autorité" pour le cache plus qu'une fois... toutes les requêtes vers le même hôte devraient pouvoir utiliser la même entrée de cache.

6
répondu JaredReisinger 2010-07-21 00:12:03

Code pris de ce post a fonctionné parfaitement pour moi implémenter L'authentification Digest via HttpWebRequest en C#

j'ai eu le problème suivant, quand jamais je browser l'url de flux dans un browser il a demandé le nom d'utilisateur et le mot de passe et a fonctionné très bien, cependant n'importe lequel des échantillons de code ci-dessus ne fonctionnaient pas, sur l'inspection D'en-tête de demande/réponse (dans les outils de développeur web dans firefox) je pouvais voir en-tête ayant L'autorisation de type digest.

Etape 1 Ajouter:

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();
    }
}

}

Étape 2: l'Appel de la nouvelle méthode

DigestAuthFixer digest = new DigestAuthFixer(domain, username, password);
string strReturn = digest.GrabResponse(dir);
http://xyz.rss.com/folder/rss puis domaine:http://xyz.rss.com(partie du domaine) dir: /dossier/flux rss (reste de l'url)

vous pouvez également le retourner en stream et utiliser la méthode XmlDocument Load ().

3
répondu Mvg Developer 2017-05-23 12:00:20

La solution est d'activer ce paramètre dans apache:

    BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On 



Complément d'information:http://httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie

puis ajouter cette propriété dans votre code pour l'objet webrequest:

    request.UserAgent = "MSIE"

il fonctionne très bien pour moi

1
répondu drmed 2012-01-04 20:21:58

je pense que la deuxième URL pointe vers la page dynamique et vous devriez d'abord l'appeler en utilisant GET to get the HTML et ensuite le télécharger. Pas d'expérience dans ce domaine.

0
répondu Neutralizer 2010-07-18 20:47:06