2010-07-03 2 views
17

Различные изделия (1, 2) я обнаружил сделать это выглядит достаточно просто:Как я могу выполнить проверку подлинности с помощью HttpWebRequest?

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; 

Однако, это работает только для URL-адресов без параметров URL. Например, я могу скачать http://example.com/test/xyz.html просто отлично, но когда я пытаюсь загрузить http://example.com/test?page=xyz, то результат будет 400 Bad Request сообщение со следующим в логах сервера (под управлением Apache 2.2):

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

Моя первая идея была что спецификация дайджеста требует, чтобы параметры URL были удалены из хэша дайджеста, но удаление параметра из URL-адреса, переданного в credentialCache.Add(), не изменило ничего. Так что это должно быть наоборот, и где-то в .NET Framework неправильно удаляет параметр из URL-адреса.

+0

Вот аналогичный вопрос на SO Мой первоначальный поиск не придумал: http://stackoverflow.com/questions/3109507/httpwebrequests-sends-parameterless-uri-in-authorization-header – Cygon

+0

И Microsoft Connect отчет об ошибке: https://connect.microsoft.com/VisualStudio/feedback/details/571052/digest-authentication-does-not-send-the-full-uri-path-in-the-uri-parameter – Cygon

+0

Microsoft Connect сообщение об ошибке, связанное выше, похоже, обходное решение, опубликованное 6/26. Вы пробовали это? –

ответ

0

Я думаю, что второй URL-адрес указывает на динамическую страницу, и вы должны сначала позвонить ему, используя GET, чтобы получить HTML-код, а затем загрузить его. Однако опыта в этой области нет.

+0

Простите, нет. Это полностью зависит от веб-сервера, что делать с URL-адресом, а первая страница также может быть динамичной. Кроме того, HTML - это то, что загружено, нет никакой разницы между загрузкой HTML или загрузкой чего-то другого. – Cygon

4

Вы сказали, что удалили параметры querystring, но попытались ли вы полностью вернуться к хозяину? Каждый пример CredentialsCache.Add(), который я видел, кажется, использует только хост, а документы для CredentialsCache.Add() перечисляют параметр Uri как «uriPrefix», который, кажется, говорит.

Другими словами, попробуйте это:

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; 

Если это работает, вы также должны убедиться, что вы не добавить тот же «власть» в кэш больше, чем когда-то ... все запросы на один и тот же хост должны иметь возможность использовать одну и ту же запись кэша учетных данных.

+0

Странно, я не сталкивался с одним примером, используя только корневой URI для аутентификации. В любом случае, это не работает, извините. В соответствии с разделом 3.2.2 RFC 2617 (http://rfc.askapache.com/rfc2617/rfc2617.html#section-3.2.2) URI дайджеста должен быть идентичен «request-uri» в HTTP-запросе. – Cygon

+0

Вот несколько примеров: 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 (хотя, по общему признанию, это пример «localhost»). – JaredReisinger

+0

Да, RFC говорит, что дайджест-uri должен соответствовать запросу, но это то, что отправлено на проводе, а не то, что хранится в кеше. В документе CredentialCache.GetCredential() (http://msdn.microsoft.com/en-us/library/fy4394xd.aspx) говорится, что «GetCredential использует самый длинный соответствующий URI-префикс в кеше, чтобы определить, какой набор учетных данных для возврата тип авторизации ". Затем он показывает, что передача домена приведет к тому, что учетные данные будут использоваться для * всех * ресурсов в этом домене. – JaredReisinger

1

Решение активировать этот параметр в апача:

BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On 


Подробнее: http://httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie

Затем добавить это свойство в коде для WebRequest объекта:

request.UserAgent = "MSIE" 

это работает очень хорошо для меня

+0

Да, см. Мой собственный комментарий по первому вопросу от 21 июля 2010 года. Это только вариант, когда вы получили контроль над сервером, и это немного раздражает меня, что мое приложение должно идентифицировать себя как MSIE, хотя;) – Cygon

2

кода взят из этой должности работал отлично для меня Implement Digest authentication via HttpWebRequest in C#

я следующий вопрос, когда я когда-либо браузер исходного адреса в браузере он спросил имя пользователя и пароль и работали нормально, однако ни один из вышеперечисленных примеров кода не работал, при проверке заголовка запроса/ответа (в инструментах веб-разработчика в firefox) я мог видеть заголовок с авторизацией типа digest.

Шаг 1 Добавить:

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

}

Шаг 2: Вызов новый метод

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

если Url является: http://xyz.rss.com/folder/rss затем домен: http://xyz.rss.com (доменная часть) реж :/folder/rss (остальная часть URL)

вы также можете вернуть его как поток и использовать метод XmlDocument Load().

+0

Отличная статья. У меня есть один вопрос: я получаю var wwwAuthenticateHeader = ex.Response.Headers ["WWW-Authenticate"]; как null, в чем причина? –

Смежные вопросы