2013-04-16 14 views
43

Modifica: volevo tornare a notare che il problema non era affatto la mia fine, ma piuttosto con il codice sul lato della società.Webclient/HttpWebRequest con autenticazione di base restituisce 404 non trovato per URL valido

Sto tentando di aprire una pagina utilizzando l'autenticazione di base. Continuo a ricevere un errore 404 Page non trovato. Posso copiare e incollare il mio url nel browser e funziona bene (se non sono loggato nel loro sito già compare una finestra di credenziali, altrimenti apre quello che voglio che apra). Devo essere nel posto giusto e autenticare, perché ottengo un 401 (errore non autenticato) se inserisco intenzionalmente un nome utente/password errato e ottengo un errore interno del server 500 se lo passo con un parametro errato nella stringa di query . Ho provato a utilizzare Webclient e HttpWebRequest che portano entrambi allo stesso errore 404 non trovato.

Con Webclient:

 string url = "MyValidURLwithQueryString"; 
     WebClient client = new WebClient(); 
     String userName = "myusername"; 
     String passWord = "mypassword"; 
     string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + ":" + passWord)); 
     client.Headers[HttpRequestHeader.Authorization] = "Basic " + credentials; 
     var result = client.DownloadString(url); 
     Response.Write(result); 

Con HttpWebRequest

 HttpWebRequest request = (HttpWebRequest)WebRequest.Create("MyValidURL"); 
     string authInfo = "username:password"; 
     authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo)); 
     request.Headers.Add("Authorization", "Basic " + authInfo); 
     request.Credentials = new NetworkCredential("username", "password"); 
     request.Method = WebRequestMethods.Http.Get; 
     request.AllowAutoRedirect = true; 
     request.Proxy = null; 
     HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 
     Stream stream = response.GetResponseStream(); 
     StreamReader streamreader = new StreamReader(stream); 
     string s = streamreader.ReadToEnd(); 
     Response.Write(s); 
+1

Utilizzando [Fiddler] (http://fiddler2.com/), accedere al sito tramite il browser e vedere quali intestazioni/corpo invia il browser. A mio avviso, dovresti anche impostare 'UserAgent' – I4V

+0

Le informazioni dell'intestazione sono corrette nel fiddler e ho anche provato a impostare UserAgent a un punto di un post precedente che avevo visto che suggeriva la stessa cosa. –

+0

Il 404 non proviene da WebClient - è dal server. C'è qualcosa che il server non apprezza dal tuo programma, che gli piace dal browser. Usa Fiddler per rendere il tuo WebClient aspetto _exactly_ come il browser. –

risposta

20

Provare a cambiare la parte richiesta di autenticazione client Web a:

NetworkCredential myCreds = new NetworkCredential(userName, passWord); 
client.Credentials = myCreds; 

Poi fare la chiamata, sembra funzionare bene per me.

+0

Ho provato a impostare la proprietà credenziali e dà lo stesso problema. –

+0

Ha funzionato per me. – matrix

58
//BEWARE 
//This works ONLY if the server returns 401 first 
//The client DOES NOT send credentials on first request 
//ONLY after a 401 
client.Credentials = new NetworkCredential(userName, passWord); //doesnt work 

//So use THIS instead to send credentials RIGHT AWAY 
string credentials = Convert.ToBase64String(
    Encoding.ASCII.GetBytes(userName + ":" + password)); 
client.Headers[HttpRequestHeader.Authorization] = string.Format(
    "Basic {0}", credentials); 
+0

Wow! una nota importante in merito al metodo descritto da @Blake non funziona fino alla seconda richiesta. Complimenti per la soluzione – Korayem

+0

Ho avuto problemi con un problema simile e questo è stato completamente risolto. GRAZIE! – onekidney

+0

ottenendo ancora 404 – Dmitry

7

Questa parte del codice ha funzionato bene per me:

 WebRequest request = WebRequest.Create(url); 
     request.Method = WebRequestMethods.Http.Get; 
     NetworkCredential networkCredential = new NetworkCredential(logon, password); // logon in format "domain\username" 
     CredentialCache myCredentialCache = new CredentialCache {{new Uri(url), "Basic", networkCredential}}; 
     request.PreAuthenticate = true; 
     request.Credentials = myCredentialCache; 
     using (WebResponse response = request.GetResponse()) 
     { 
      Console.WriteLine(((HttpWebResponse)response).StatusDescription); 

      using (Stream dataStream = response.GetResponseStream()) 
      { 
       using (StreamReader reader = new StreamReader(dataStream)) 
       { 
        string responseFromServer = reader.ReadToEnd(); 
        Console.WriteLine(responseFromServer); 
       } 
      } 
     } 
2

Se il suo lavoro quando si utilizza un browser e poi passando sul tuo nome utente e password per la prima volta - allora questo significa che una volta l'autenticazione viene eseguita L'intestazione richiesta del browser viene impostata con i valori di autenticazione richiesti, che vengono poi passati ogni volta che viene effettuata una richiesta al server di hosting.

Quindi, inizia con l'ispezione dell'intestazione della richiesta (questo potrebbe essere fatto utilizzando gli strumenti degli sviluppatori Web), una volta stabilito quali sono richiesti nell'intestazione, è possibile passare questo all'interno dell'HttpWebRequest Header.

Esempio con autenticazione Digest:

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

}

Poi si può chiamare:

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

se l'URL è: http://xyz.rss.com/folder/rss allora dominio: http://xyz.rss.com (parte del dominio) dir:/folder/rss (rest of the url)

potresti anche restituirlo come stream e usare il metodo XmlDocument Load().

Problemi correlati