2014-09-10 3 views
14

È possibile eseguire un servizio Web REST con API che richiede che i client utilizzino l'autenticazione di base. Abbiamo creato una serie di campioni accurati in varie lingue che mostrano come interfacciarsi con il nostro servizio. Ora sto rivedendo registri di IIS del servizio e vedere che il seguente schema accade abbastanza spesso:Perché il mio servizio REST, i client .NET, inviano ogni richiesta senza intestazioni di autenticazione e quindi ritentano con l'intestazione di autenticazione?

  • arriva una richiesta, viene respinto con il codice HTTP 401
  • la stessa richiesta viene inviato nuovamente e riesce

che sembra che la prima richiesta venga inviata senza Autorizzazione intestazioni e quindi la seconda viene inviata con le intestazioni corrette e ha esito positivo. La maggior parte delle volte il registro contiene "user-agent", la stessa stringa che abbiamo inserito nel nostro esempio .NET.

Quindi presumo che il problema sia solo con i programmi .NET. Il problema non è riprodotto con il nostro codice di esempio, quindi presumo che gli utenti abbiano modificato il codice in qualche modo o ne abbiano scritto da zero.

Abbiamo provato a contattare gli utenti ma a quanto pare non vogliono investire tempo nella ricerca. Quindi sarebbe bello scoprire quale sia lo scenario più probabile che porta a questo comportamento dei programmi .NET.

Perché dovrebbero farlo? Perché non dovrebbero attaccare le intestazioni al primo tentativo?

risposta

31

Questo è il comportamento predefinito delle classi HttpClient e HttpWebRequest che viene esposto nel seguente modo.

Nota: il testo sottostante spiega il comportamento subottimale causando il problema descritto nella domanda. Molto probabilmente non dovresti scrivere il tuo codice in questo modo. Invece scorrere sotto per il codice corretto

In entrambi i casi, un'istanza di un oggetto NetworkCredenatial e impostare il nome utente e la password in là

var credentials = new NetworkCredential(username, password); 

Se si utilizza HttpWebRequest - impostare .Credentials proprietà:

webRequest.Credentials = credentials; 

Se si utilizza HttpClient - passare l'oggetto credenziali in HttpClientHandler (codice modificato da here):

var client = new HttpClient(new HttpClientHandler() { Credentials = credentials }) 

Quindi eseguire Fiddler e avviare la richiesta.Vedrete la seguente:

  • la richiesta viene inviata senza Autorizzazione intestazione
  • il servizio risponde con HTTP 401 e WWW-Authenticate: Base regno = "UrRealmHere"
  • la richiesta viene reinviato con una corretta Autorizzazione intestazione (e riesce)

Questo comportamento è spiegato here - il clie nt non sa in anticipo che il servizio richiede Basic e tenta di negoziare il protocollo di autenticazione (e se il servizio richiede Digest l'invio di intestazioni di base in open è inutile e può compromettere il client).

Nota: Qui la spiegazione del comportamento subottimale termina e viene spiegato un approccio migliore. Molto probabilmente dovresti usare il codice dal basso invece del codice dall'alto.

Per i casi in cui è noto che il servizio richiede base tale richiesta supplementare può essere eliminato nel modo seguente:

Non impostare .Credentials, invece aggiungere le intestazioni manualmente utilizzando il codice da here. Codificare il nome utente e la password:

var encoded = Convert.ToBase64String(Encoding.ASCII.GetBytes(
    String.Format("{0}:{1}", username, password))); 

Quando si utilizza HttpWebRequest aggiungerlo alle intestazioni:

request.Headers.Add("Authorization", "Basic " + encoded); 

e quando si utilizza HttpClient aggiungerlo alle intestazioni predefinite:

client.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue("Basic", encoded); 

quando fai così la richiesta viene inviata ogni volta con le intestazioni di autorizzazione corrette. Si noti che è non dovrebbe impostare .Credentials, altrimenti se il nome utente o la password sono errati la stessa richiesta verrà inviata due volte sia il tempo con le credenziali errate ed entrambe le volte, naturalmente, cedendo HTTP 401.

+1

Questa soluzione funziona bene come consente di impostare l'intestazione di autorizzazione sulla prima richiesta, evitando così la sfida/risposta 401. Nella mia esperienza con HttpClient e HttpClientHandler, tuttavia, l'impostazione di pre-autenticazione funziona in quanto l'intestazione Autorizza è impostata su ogni richiesta dopo il completamento della Sfida/Risposta 401. Questo articolo lo spiega bene: http://weblog.west-wind.com/posts/2010/Feb/18/NET-WebRequestPreAuthenticate-not-quite-what-it-sounds-like – Philippe

+0

@Philippe: Sì, funziona pure. Grazie. – sharptooth

+0

Suggerimento: per il proxy si dovrebbe invece impostare l'intestazione 'client.DefaultRequestHeaders.ProxyAuthorization'. – stil