2015-09-21 15 views
5

come dice già la domanda, sto cercando di fare l'autenticazione digest in Android.
Fino ad ora ho usato il DefaultHttpClient ed è metodo di autenticazione (utilizzando UsernamePasswordCredentials e così via), ma è sconsigliato in quanto Android 5 e sarà rimosso in Android 6.
Quindi sto per passare dalla DefaultHttpClient a HttpUrlConnection.
Ora sto cercando di ottenere l'autenticazione del digest, che dovrebbe funzionare abbastanza semplici come spiegato here:Autenticazione del digest in Android utilizzando HttpURLConnection

Authenticator.setDefault(new Authenticator() { 
    protected PasswordAuthentication getPasswordAuthentication() { 
     return new PasswordAuthentication(username, password); 
    } 
}); 

Ma la getPasswordAuthentication ottiene mai chiamato per qualche motivo.
Durante la mia ricerca di questo problema ho trovato post diversi, dicendo che l'autenticazione digest non è supportata dallo HttpUrlConnection in Android, ma quei post sono del 2010-2012, quindi non sono sicuro che questo sia ancora vero. Inoltre stiamo usando HttpUrlConnection con l'autenticazione digest nella nostra applicazione java desktop, dove funziona.

Ho anche trovato alcuni post, parlando di OkHttp. OkHttp sembra essere utilizzato da Android sotto il cofano (per essere più specifici il HttpUrlConnectionImpl). Ma questo HttpUrlConnectionImpl è un po 'strano, non è nemmeno mostrato nella gerarchia di tipo Eclipse e non sono in grado di eseguirne il debug. Inoltre dovrebbe essere un com.squareup.okhttp.internal.huc.HttpUrlConnectionImpl, mentre in Android è un com.android.okhttp.internal.http.HttpUrlConnectionImpl.

Quindi non sono in grado di eseguire l'autenticazione digest con questo HttpUrlConnection in Android.
Qualcuno può dirmi come farlo senza librerie esterne?

EDIT:
Il server chiede l'autenticazione digest:

WWW-Authenticate: Digest realm="Realm Name",domain="/domain",nonce="nonce",algorithm=MD5,qop="auth" 

Così Basic-autenticazione shouldn' di lavoro, come il server sta chiedendo digest.

risposta

3

La risposta è, che HttpUrlConnection non supporta digest.

È quindi necessario implementare RFC2617 da soli.

È possibile utilizzare il seguente codice come implementazione di base: HTTP Digest Auth for Android.

I passaggi implicano (vedi RFC2617 per riferimento):

  • Se si ottiene una risposta 401, iterare su tutti WWW-Authenticate le intestazioni e le analisi:
    • Controllare se algoritmo è MD5 o non definito, (opzionalmente selezionare l'opzione qop auth), altrimenti ignorare la sfida e andare alla successiva intestazione.
    • Ottieni le credenziali utilizzando Authenticator.requestPasswordAuthentication.
    • Calcolare H (A1) utilizzando lo username, il realm e la password.
    • Memorizza l'URL radice canonico, il dominio, HA1, nome utente, nonce (+ facoltativamente algoritmo, opaco e l'opzione qop selezionata dal cliente se presente).
    • Riprovare la richiesta.
  • Su ogni richiesta, iterare su tutti i regni si dispone di informazioni di sessione memorizzati dal URL principale canonica:
    • Calcolare H (A2) utilizzando il metodo di richiesta e il percorso.
    • Calcolare H (A3) utilizzando HA1, nonce (+ opzionalmente nc, cnonce, qop) e HA2.
    • Costruisci e aggiungi l'intestazione Authorization al numero HttpUrlConnection.
  • Implementare una sorta di potatura della sessione.

Utilizzando Authenticator, è possibile assicurarsi, che non appena HttpUrlConnection supporti digerire in modo nativo, non viene utilizzato il codice di più (perché è solito riceve il 401, in primo luogo).

Questo è solo un breve riepilogo su come implementarlo, per farti un'idea.

Se si vuole andare più si sarebbe probabilmente piace implementare SHA256 così: RFC7616

+0

Grazie per questa risposta. È solo la versione di Androids di 'HttpUrlConnection', che non supporta digest o anche il' default java.net.HttpURLConnection'? – Springrbua

+0

@Springrbua Solo su Android non è supportato, almeno per quanto ne so. Forse troverai fonti per l'implementazione di JDK. – Nappy

+0

Ok, grazie per la risposta. Continuerò con il "DefaultHttpClient" per ora, ma sembra che ho bisogno di passare prima o poi quindi dovrò implementare digerire me stesso. Grazie! – Springrbua

1

Forse si tenta di impostare manualmente l'intestazione come:

String basic = "Basic " + new String(Base64.encode("username:password".getBytes(),Base64.NO_WRAP)); 
connection.setRequestProperty ("Authorization", basic); 

essere a conoscenza di alcuni problemi in Jellybeans e un bug quando si tenta di eseguire una richiesta POST anche: HTTP Basic Authentication issue on Android Jelly Bean 4.1 using HttpURLConnection

EDIT: Per L'autenticazione del digest

Date un'occhiata qui https://code.google.com/p/android/issues/detail?id=9579

Espec buona resa questo potrebbe funzionare:

try { 
     HttpClient client = new HttpClient(
       new MultiThreadedHttpConnectionManager()); 

     client.getParams().setAuthenticationPreemptive(true); 
     Credentials credentials = new UsernamePasswordCredentials("username", "password"); 
     client.getState().setCredentials(AuthScope.ANY, credentials); 
     List<String> authPrefs = new ArrayList<String>(2); 
     authPrefs.add(AuthPolicy.DIGEST); 
     authPrefs.add(AuthPolicy.BASIC); 
     client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, 
       authPrefs); 
     GetMethod getMethod = new GetMethod("your_url"); 
     getMethod.setRequestHeader("Accept", "application/xml"); 
     client.executeMethod(getMethod); 
     int status = getMethod.getStatusCode(); 
     getMethod.setDoAuthentication(true); 
     System.out.println("status: " + status); 
     if (status == HttpStatus.SC_OK) { 
      String responseBody = getMethod.getResponseBodyAsString(); 
      String resp = responseBody.replaceAll("\n", " "); 
      System.out.println("RESPONSE \n" + resp); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
+1

Ho modificato la mia domanda e aggiunto l'intestazione WWW-Authenticate, fornita dal server. Dice 'digest', quindi gguess si aspetta e richiede l'autenticazione digest ... – Springrbua

+0

Ceck, ho cambiato la mia risposta, spero che aiuti. Sembra che ci siano alcuni bug nel 'Authenticator' su Android –

+0

Controlla anche http://stackoverflow.com/questions/2954434/apache-httpclient-digest-authentication –

2

È vero che HttpUrlConnection non supporta l'autenticazione Digest. Se il tuo cliente deve autenticarsi utilizzando Digest, hai alcune opzioni:

  • Scrivi la tua implementazione HTTP Digest. Questa può essere una buona opzione se si conoscono i server con cui è necessario autenticarsi e possono ignorare le parti della specifica digest che non sono necessarie. Ecco un esempio in cui è implementato un sottoinsieme di digest: https://gist.github.com/slightfoot/5624590.
  • Utilizzare il lib esterno bare-bones-digest, che è un lib di Digest per Android. Puoi usarlo per analizzare le sfide del Digest e generare risposte ad esse. Supporta i casi di utilizzo di digest comuni e alcuni di quelli usati raramente e può essere utilizzato in cima a HttpURLConnection.
  • Utilizzare OkHttp insieme a okhttp-digest, che è un plug-in che aggiunge il supporto Http Digest a OkHttp. Supportare Digest con OkHttp è facile, basta aggiungere okhttp-digest come autenticatore e si avrà il supporto per il digest Http trasparente. Se già usi OkHttp o stai bene con il passaggio ad esso, questa può essere un'opzione interessante.
  • Utilizzare Apache HttpClient che supporta Digest. La domanda afferma esplicitamente che HttpClient non è un'opzione, quindi la includo principalmente per il completamento. Google sconsiglia l'uso di HttpClient e lo ha deprecato.
+0

Ho sostituito 'DefaultHttpClient' con' HttpUrlConnection' alcuni mesi fa e ho implementato l'autenticazione 'Digest authentication', usando (questo) [https://gist.github.com/slightfoot/5624590] come modello. Potrei aggiungere il mio codice come risposta, potrebbe aiutare qualcun altro! – Springrbua

1

fine ho sostituito il deprecato DefaultHttpClient con la mia propria implementazione del HttpUrlConnection e ho implementato digest atuhentication me stesso, utilizzando this come modello.
Il codice finalmente sembra qualcosa di simile:

// requestMethod: "GET", "POST", "PUT" etc. 
// Headers: A map with the HTTP-Headers for the request 
// Data: Body-Data for Post/Put 
int statusCode = this.requestImpl(requestMethod, headers, data); 
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED && hasUserNameAndPassword) { 
    String auth = getResponseHeaderField("WWW-Authenticate"); 
    // Server needs Digest authetication 
    if(auth.startsWith("Digest")){ 
      // Parse the auth Header 
      HashMap<String, String> authFields = parseWWWAuthenticateHeader(auth); 
      // Generate Auth-Value for request 
      String requestAuth = generateDigestAuth(authFields); 
      headers.put("Authorization", authStr); 
      statusCode = this.requestImpl(requestMethod, headers, data); 
    } 
} 

Quindi fondamentalmente Faccio una richiesta e se restituisce 401, guardo, se il server vuole digest authentication e se ho username e password. Se questo è il caso, analizzo l'intestazione auth della risposta, che contiene tutte le informazioni necessarie sull'autenticazione.
Per analizzare l'intestazione auth, utilizzo un tipo di StateMachine che è descritto here.
Dopo l'analisi della intestazione di risposta di autenticazione, che generano l'intestazione della richiesta di autenticazione utilizzando le informazioni dalla risposta:

String digestAuthStr = null; 

    String uri = getURL().getPath(); 
    String nonce = authFields.get("nonce"); 
    String realm = authFields.get("realm"); 
    String qop = authFields.get("qop"); 
    String algorithm = authFields.get("algorithm"); 
    String cnonce = generateCNonce(); 
    String nc = "1"; 
    String ha1 = toMD5DigestString(concatWithSeparator(":", username, realm, password)); 
    String ha2 = toMD5DigestString(concatWithSeparator(":", requestMethod, uri)); 
    String response = null; 
    if (!TextUtils.isEmpty(ha1) && !TextUtils.isEmpty(ha2)) 
     response = toMD5DigestString(concatWithSeparator(":", ha1, nonce, nc, cnonce, qop, ha2)); 

    if (response != null) { 
     StringBuilder sb = new StringBuilder(128); 
     sb.append("Digest "); 
     sb.append("username").append("=\"").append(username).append("\", "); 
     sb.append("realm").append("=\"").append(realm).append("\", "); 
     sb.append("nonce").append("=\"").append(nonce).append("\", "); 
     sb.append("uri").append("=\"").append(uri).append("\", "); 
     sb.append("qop").append("=\"").append(qop).append("\", "); 
     sb.append("nc").append("=\"").append(nc).append("\", "); 
     sb.append("cnonce").append("=\"").append(cnonce).append("\""); 
     sb.append("response").append("=\"").append(response).append("\""); 
     sb.append("algorithm").append("=\"").append(algorithm).append("\""); 
     digestAuthStr = sb.toString(); 
    } 

per generare il client-Nonce Sto usando il seguente codice:

private static String generateCNonce() { 
    String s = ""; 
    for (int i = 0; i < 8; i++) 
     s += Integer.toHexString(new Random().nextInt(16)); 
    return s; 
} 

I spero che questo aiuti qualcuno. Se il codice contiene errori, per favore fatemelo sapere così posso ripararlo. Ma adesso sembra funzionare.

+0

Questa è un'implementazione digest molto efficiente ed efficiente, ma è bene essere consapevoli che implementa solo un piccolo sottoinsieme di digest. Questo va bene se tu, per esempio, lavori solo con un server particolare e puoi testarlo. Se devi essere compatibile con molti server diversi e gestire cose come sfide multiple, caratteri di escape nelle stringhe tra virgolette, digest SHA-256, direttiva opaque, riutilizzo di sfide, ecc, allora probabilmente stai meglio usare una delle librerie (bare-bones-digest, okhtt-digest, Apache HttpClient, ecc.) per coprire i casi d'angolo. – user829876

+0

@ user829876 Sono completamente d'accordo con te. Se hai bisogno del pieno supporto di digest, dovresti assolutamente andare con una libreria conosciuta e quindi testata. – Springrbua

Problemi correlati