c# - ¿Cómo puedo hacer la autenticación de resumen con HttpWebRequest?
authentication digest (4)
Varios artículos ( 1 , 2 ) que descubrí hacen que esto parezca bastante fácil:
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;
Sin embargo, esto solo funciona para URL sin parámetros de URL. Por ejemplo, puedo descargar http://example.com/test/xyz.html
muy bien, pero cuando intento descargar http://example.com/test?page=xyz
, el resultado es un mensaje de 400 solicitudes incorrectas con lo siguiente en los registros del servidor (ejecutando Apache 2.2):
Digest: uri mismatch - </test> does not match request-uri </test?page=xyz>
Mi primera idea fue que la especificación de resumen requiere que los parámetros de URL se eliminen del hash de resumen, pero eliminar el parámetro de la URL que se pasó a credentialCache.Add()
no cambió nada. Por lo tanto, debe ser al revés y en algún lugar del marco .NET se está eliminando erróneamente el parámetro de la URL.
Creo que la segunda URL apunta a una página dinámica y primero debe llamarlo usando GET para obtener el HTML y luego descargarlo. Sin embargo, no hay experiencia en este campo.
El código tomado de esta publicación me ha funcionado perfectamente. Implemente la autenticación mediante Digest a través de HttpWebRequest en C #
Tuve el siguiente problema: cuando exploraba la URL del feed en un navegador, pedía un nombre de usuario y una contraseña y funcionaba bien. Sin embargo, cualquiera de los ejemplos de código anteriores no funcionaban, al inspeccionar el encabezado de solicitud / respuesta (en las herramientas de desarrollador web en Firefox) Pude ver el encabezado con Autorización de tipo de resumen.
Paso 1 Añadir:
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();
}
}
}
Paso 2: Llamar nuevo método
DigestAuthFixer digest = new DigestAuthFixer(domain, username, password);
string strReturn = digest.GrabResponse(dir);
si la URL es: http://xyz.rss.com/folder/rss entonces domain: http://xyz.rss.com (parte del dominio) dir: / folder / rss (resto de la url)
también puede devolverlo como flujo y usar el método XmlDocument Load ().
La solución es activar este parámetro en apache:
BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On
Más información: httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie
Luego agregue esta propiedad en su código para el objeto de solicitud de web:
request.UserAgent = "MSIE"
funciona muy bien para mi
Usted dijo que eliminó los parámetros de la cadena de consulta, pero ¿intentó volver al host solo? Todos los ejemplos de CredentialsCache.Add () que he visto parecen usar solo el host, y los documentos para CredentialsCache.Add() listan el parámetro Uri como "uriPrefix", lo que parece ser revelador.
En otras palabras, prueba esto:
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 esto funciona, también deberá asegurarse de no agregar la misma "autoridad" a la memoria caché más de una vez ... todas las solicitudes al mismo host deben poder utilizar la misma entrada de memoria caché de credenciales.