2015-09-21 18 views
5

wie die Frage bereits sagt, ich versuche Digest-Authentifizierung in Android zu tun.
Bis jetzt habe ich die DefaultHttpClient und es ist Authentifizierungsmethode (unter Verwendung von UsernamePasswordCredentials und so weiter), aber es ist veraltet, da Android 5 und werden entfernt in Android 6.
Also ich bin wechseln von DefaultHttpClient zu HttpUrlConnection verwendet.
Jetzt versuche ich Digest-Authentifizierung zu erreichen, die als here erklärt ziemlich einfach funktionieren sollte:Digest-Authentifizierung in Android mit HttpURLConnection

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

Aber die getPasswordAuthentication wird nie aus irgendeinem Grund genannt.
Während meiner Suche nach diesem Problem fand ich verschiedene Beiträge, sagen Digest Authentifizierung wird nicht von der HttpUrlConnection in Android unterstützt, aber diese Beiträge sind von 2010-2012, so dass ich nicht sicher bin, ob dies immer noch wahr ist. Auch verwenden wir HttpUrlConnection mit Digest-Authentifizierung in unserer Desktop-Java-Anwendung, wo es funktioniert.

Ich fand auch einige Beiträge, über OkHttp sprechen. OkHttp scheint von Android unter der Haube (um genauer zu sein die HttpUrlConnectionImpl) verwendet werden. Aber das HttpUrlConnectionImpl ist ein bisschen seltsam, es ist nicht einmal in der Eclipse-Typ-Hierarchie angezeigt und ich bin nicht in der Lage, es zu debuggen. Auch sollte es ein com.squareup.okhttp.internal.huc.HttpUrlConnectionImpl sein, während es in android ist ein com.android.okhttp.internal.http.HttpUrlConnectionImpl.

So kann ich Digest Authentifizierung mit diesem HttpUrlConnection in Android einfach nicht tun.
Kann mir jemand sagen, wie man das ohne externe Bibliotheken macht?

EDIT:
Der Server fordert für Digest-Authentifizierung:

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

So Basis-Authentifizierung shouldn‘Arbeit, da der Server für Digest fragt.

Antwort

3

Die Antwort ist, dass HttpUrlConnection nicht verdauen unterstützen.

Sie müssen daher RFC2617 selbst implementieren.

Sie können den folgenden Code als Basisimplementierung verwenden: HTTP Digest Auth for Android.

Die Schritte umfassen (RFC2617 Referenz sehen):

  • Wenn Sie eine 401-Antwort erhalten, durchlaufen alle WWW-Authenticate Header und analysieren sie:
    • Überprüfen Sie, ob Algorithmus MD5 oder nicht definiert ist, (Wählen Sie optional die Option auth qop. Andernfalls ignorieren Sie die Abfrage und gehen Sie zur nächsten Kopfzeile.
    • Holen Sie sich die Anmeldeinformationen mit Authenticator.requestPasswordAuthentication.
    • Berechnen Sie H (A1) mit dem Benutzernamen, Realm und Passwort.
    • Speichern Sie die kanonische Stamm-URL, Realm, HA1, Benutzername, Nonce (+ optional Algorithmus, opak und die Client-Option qop, falls vorhanden).
    • Wiederholen Sie die Anfrage.
  • bei jeder Anfrage, alle Bereiche iterieren Sie Sitzungsinformationen für durch kanonische root URL gespeichert haben:
    • berechnen H (A2) unter Verwendung des Anforderungsverfahren und den Pfad.
    • Berechnen Sie H (A3) mit HA1, Nonce (+ optional nc, Cannon, Qop) und HA2.
    • Build und fügen Sie die Authorization Header zu Ihrem HttpUrlConnection.
  • Implementieren Sie eine Art von Session-Beschneidung.

von Authenticator verwenden, können Sie sicherstellen, dass sobald HttpUrlConnection unterstützt nativ verdauen, wird der Code nicht mehr verwendet wird (weil Sie nicht die 401 in erster Linie erhalten).

Dies ist nur eine kurze Zusammenfassung, wie Sie es implementieren, damit Sie eine Idee bekommen.

Wenn Sie weiter gehen wollen würden Sie wahrscheinlich SHA256 implementieren möchten auch: RFC7616

+0

Danke für diese Antwort. Ist es nur die Android-Version von 'HttpUrlConnection', die digest oder auch den Standard' java.net.HttpURLConnection' nicht unterstützt? – Springrbua

+0

@Springrbua Nur auf Android wird es nicht unterstützt, zumindest meines Wissens. Vielleicht finden Sie Quellen für die JDK-Implementierung. – Nappy

+0

Okay danke für deine Antwort. Ich bleibe jetzt beim 'DefaultHttpClient', aber es sieht so aus, als müsste ich früher oder später wechseln, also muss ich selbst Digest implementieren. Vielen Dank! – Springrbua

1

Haben Sie versucht, den Header manuell einstellen wie:

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

Auch bewusst sein, einige Probleme in Jellybeans und einen Fehler, wenn Sie versuchen, eine Post-Anforderung auszuführen: HTTP Basic Authentication issue on Android Jelly Bean 4.1 using HttpURLConnection

EDIT: Für Digest-Authentifizierung

Werfen Sie einen Blick hier https://code.google.com/p/android/issues/detail?id=9579

Espez mathematisch könnte diese Arbeit:

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

Ich bearbeite meine Frage und fügte den vom Server angegebenen WWW-Authenticate-Header hinzu. Es sagt 'verdauen', also ich gguess es erwarten und Digestauthentifizierung erfordert ... – Springrbua

+0

Ceck, änderte meine Antwort, hoffe, dass es hilft. Es scheint ein paar Bugs im "Authenticator" auf Android –

+0

zu geben Überprüfen Sie auch http://StackOverflow.com/questions/2954434/apache-httpclient-digest-Authentication –

2

Es ist richtig, dass HttpUrlConnection nicht Digest-Authentifizierung nicht unterstützt. Wenn Ihr Client mit Digest authentifizieren muss, haben Sie einige Optionen:

  • Schreiben Sie Ihre eigene HTTP Digest-Implementierung. Dies kann eine gute Option sein, wenn Sie wissen, mit welchen Servern Sie sich authentifizieren müssen, und die Teile der Digest-Spezifikation ignorieren können, die Sie nicht benötigen. Hier ist ein Beispiel, in dem eine Teilmenge von Digest implementiert ist: https://gist.github.com/slightfoot/5624590.
  • Verwenden Sie die externe Lib bare-bones-digest, die eine Digest lib für Android ist. Sie können damit Digest-Herausforderungen analysieren und Antworten darauf generieren. Es unterstützt die üblichen Digest Use Cases und einige der selten verwendeten und kann auf HttpURLConnection verwendet werden.
  • Verwenden Sie OkHttp zusammen mit okhttp-digest, das ist ein Plugin, das Http Digest-Unterstützung zu OkHttp hinzufügt. Das Unterstützen von Digest mit OkHttp ist einfach, fügen Sie einfach okhttp-digest als Authentifikator hinzu und Sie werden eine transparente Http Digest-Unterstützung haben. Wenn Sie OkHttp bereits verwenden oder mit dem Wechsel OK sind, kann dies eine attraktive Option sein.
  • Verwenden Sie den Apache HttpClient, der Digest unterstützt. Die Frage stellt ausdrücklich fest, dass HttpClient keine Option ist, also schließe ich es größtenteils der Vollständigkeit halber ein. Google empfiehlt nicht, HttpClient zu verwenden, und hat es veraltet.
+0

Ich habe vor ein paar Monaten den 'DefaultHttpClient' durch' HttpUrlConnection' ersetzt und habe 'Digest authentication' implementiert, indem ich (this) [https://gist.github.com/slightfoot/5624590] als Vorlage benutze. Ich könnte meinen Code als Antwort hinzufügen, könnte es jemand anderem helfen! – Springrbua

1

ich endlich ersetzt die veralteten DefaultHttpClient mit meiner eigenen Implementierung der HttpUrlConnection und ich digest atuhentication selbst implementiert, this als Vorlage verwendet wird.
Der finaly Code sieht wie folgt aus etwas:

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

Also im Grunde mache ich eine Anfrage, und wenn es 401 zurück, ich sehe, wenn der Server digest authentication will und wenn ich Benutzername und Passwort. Wenn das der Fall ist, parse ich den Auth-Header der Antwort, die alle notwendigen Informationen über die Authentifizierung enthält.
Um die Auth-Header zu analysieren, verwende ich eine Art von StateMachine, die here beschrieben wird.
Nach der Antwort Auth-Header-Parsing, ich die Anfrage Auth-Header mit den Informationen aus der Antwort erzeugen:

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

die Client-Nonce generiert ich den folgenden Code verwenden:

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

I hoffe das hilft jemandem. Wenn der Code Fehler enthält, lassen Sie es mich bitte wissen, damit ich es beheben kann. Aber gerade jetzt scheint es zu funktionieren.

+0

Dies ist eine sehr kompakte und effiziente Digest-Implementierung, aber es ist gut zu wissen, dass sie nur eine kleine Teilmenge von Digest implementiert. Das ist in Ordnung, wenn Sie beispielsweise nur mit einem bestimmten Server arbeiten und damit testen können. Wenn Sie mit vielen verschiedenen Servern kompatibel sein und Dinge wie mehrfache Herausforderungen, Escape-Zeichen in Anführungszeichenfolgen, SHA-256-Digest, die Opak-Anweisung, Wiederverwenden von Herausforderungen usw. bewältigen müssen, ist es wahrscheinlich besser, eine der Bibliotheken zu verwenden (bare-bones-digest, okhtt-digest, Apache HttpClient, etc.), um die Eckfälle abzudecken. – user829876

+0

@ user829876 Ich stimme absolut mit Ihnen überein. Wenn Sie vollständige Unterstützung von Digest benötigen, sollten Sie auf jeden Fall mit einer bekannten und daher getesteten Bibliothek gehen. – Springrbua

Verwandte Themen