2013-06-15 10 views
20

Ich bin ein Neuling auf Android und das ist mein erstes Projekt auf Android. Ich kämpfe seit mehr als einem Tag mit "Authentifizierung" Problem. Ich habe mehrere Möglichkeiten ausprobiert, aber keiner von ihnen hat funktioniert.java.io.IOException: Keine Authentifizierung Herausforderungen gefunden

Grundsätzlich möchte ich eine REST-API aufrufen und Antwort erhalten. Ich bin sicher, dass es kein Problem in der API gibt, da ich dasselbe in einer anderen iOS-Anwendung verwende.

Ich übergeben Authorization-Header, aber immer noch Authentifizierung keine gefunden Nachricht wird angezeigt. Ich habe einige Fragen zu Stack Overflow in diesem Zusammenhang, aber einige von ihnen haben nicht funktioniert und einige ergeben keinen Sinn für mich.

Ich bekomme Statuscode 401. Ich weiß, das bedeutet entweder keine Authentifizierung bestanden oder wenn bestanden, dann sind sie falsch. Hier bin ich sicher, dass meine bestandenen korrekt sind.

Unten ist mein Code:

try { 
    url = new URL(baseUrl); 
} 
catch (MalformedURLException me) { 
    Log.e(TAG, "URL could not be parsed. URL : " + baseUrl + ". Line : " + getLineNumber(), me); 
    me.printStackTrace(); 
} 

try { 
    urlConnection = (HttpURLConnection) url.openConnection(); 
    urlConnection.setRequestMethod(method); 
    urlConnection.setConnectTimeout(TIMEOUT * 1000); 
    urlConnection.setChunkedStreamingMode(0); 

    // Set HTTP headers     
    String authString = "username:password"; 
    String base64Auth = Base64.encodeToString(authString.getBytes(), Base64.DEFAULT); 
    urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth); 
    urlConnection.setRequestProperty("Accept", "application/json"); 
    urlConnection.setRequestProperty("Content-type", "application/json"); 

    if (method.equals("POST") || method.equals("PUT")) { 
     // Set to true when posting data 
     urlConnection.setDoOutput(true); 

     // Write data to post to connection output stream 
     OutputStream out = urlConnection.getOutputStream(); 
     out.write(postParameters.getBytes("UTF-8")); 
    } 

    try { 
     // Get response 
     in = new BufferedInputStream(urlConnection.getInputStream()); 
    } 
    catch (IOException e) { 
     Log.e(TAG, "Exception in getting connection input stream. in : " + in); 
        e.printStackTrace(); 
    } 

    // Read the input stream that has response 
    statusCode = urlConnection.getResponseCode(); 
    Log.d(TAG, "Status code : " + statusCode); 
} 
catch (ProtocolException pe) { 
    pe.printStackTrace(); 
} 
catch (IllegalStateException ie) { 
    ie.printStackTrace(); 
} 
catch (IOException e) { 
    e.printStackTrace(); 
} 
finally { 
    urlConnection.disconnect(); 
} 


Blick auf Screenshot von logcat:

logcat

Jede Hilfe würde geschätzt. Vielen Dank.

+0

String authString = "username: password"; dieser "username: password" sollte gleich ihren strings sein, die ich im website developers model meine. für zB: sie können Loginid verwendet werden: Passwort wie das. – manivannan

+0

@Akash haben Sie eine Lösung gefunden? – AsafK

+0

@AsafK Grund war, dass HttpUrlConnection eine Ausnahme für Statuscode 400 und höher auslösen. Wir müssen mit dieser Ausnahme umgehen. In meinem Fall habe ich HttpClient anstelle von HttpUrConnection wegen seiner Behandlung aller Statuscodes verwendet. – Geek

Antwort

1

In welcher Android-Version testen Sie?

Ich hatte Schwierigkeiten mit dem Android-Authentifikator während einiger Entwicklungsarbeiten an Gingerbread (ich weiß nicht, ob es sich bei späteren Versionen von Android anders verhält). Ich habe mit Fiddler2 den HTTP-Verkehr zwischen meiner App und dem Server untersucht und dabei festgestellt, dass der Authentifikator die Authentifizierungszeichenfolge nicht für jede HTTP-Anfrage gesendet hat. Ich brauchte es.

Stattdessen ich diese zurückgegriffen:

urlConnection.setRequestProperty("Authorization", "Basic " + Base64.encodeToString("userid:pwd".getBytes(), Base64.NO_WRAP)); 

Es ist ein Cut-and-Paste aus meinem Code. Beachten Sie, dass urlConnection ein HttpURLConnection-Objekt ist.

+0

Ich habe das versucht. Aber hat nicht funktioniert. Sogar ich habe versucht, verschiedene base64-Flags wie 'URL_SAFE',' NO_WRAP', 'DEFAULT' zu verwenden. Ich habe auch versucht "URL_SAFE | NO_WRAP'". – Geek

+0

Ich habe überprüft, dass Fiddler2 für Windows ist. Gibt es solche für MAC? – Geek

+0

Android-Version ist ICS. – Geek

45

Dieser Fehler tritt auf, weil der Server eine 401 (nicht autorisiert) sendet, aber keinen WWW-Authenticate Header gibt, der ein Hinweis für den Client ist, was als nächstes zu tun ist. Der Header WWW-Authenticate teilt dem Client mit, welche Art von Authentifizierung benötigt wird (entweder Basic oder Digest). Dies ist wahrscheinlich in kopflosen HTTP-Clients nicht sehr nützlich, aber so ist die HTTP 1.1 RFC is defined. Der Fehler tritt auf, weil die Bibliothek versucht, den Header WWW-Authenticate zu analysieren, aber nicht kann.

Von der RFC:

(...) Die Antwort ein WWW-Authenticate-Header-Feld (Abschnitt 14,47), die eine Herausforderung für die angeforderte Ressource enthalten muss (...)

.

Mögliche Lösungen, wenn Sie können den Server ändern:

  • hinzufügen fake "WWW-Authenticate" Header wie: WWW-Authenticate: Basic realm="fake". Dies ist eine bloße Problemumgehung und keine Lösung, aber es sollte funktionieren und der http-Client ist erfüllt (see here a discussion of what you can put in the header).Aber bedenken Sie, dass einige HTTP-Clients die Anfrage automatisch wiederholen können, was zu mehreren Anfragen führt (z. B. zu oft die falsche Anmeldeanzahl erhöht). Dies wurde mit dem iOS-HTTP-Client beobachtet.
  • Wie loudvchar vorgeschlagen in this blog auf die Herausforderung automatische Reaktionen wie ein in einem Browser-Pop-up-Login-Formular zu vermeiden, können Sie eine Nicht-Standard-Authentifizierungsmethode wie so verwenden: WWW-Authenticate: xBasic realm="fake". Der wichtige Punkt ist, dass die realm enthalten sein muss. Verwenden Sie den HTTP-Statuscode 403 anstelle von 401. Es ist semantisch ist nicht das gleiche und in der Regel bei der Arbeit mit Login 401 ist eine richtige Antwort (see here for a detailed discussion), aber die sicherere Lösung in Bezug auf die Kompatibilität.

Mögliche Lösungen, wenn Sie der Server nicht ändern kann:

  • Als @ErikZ in seiner post schrieb man einen Versuch & fangen

    HttpURLConnection connection = ...; 
    try { 
        // Will throw IOException if server responds with 401. 
        connection.getResponseCode(); 
    } catch (IOException e) { 
        // Will return 401, because now connection has the correct internal state. 
        int responsecode = connection.getResponseCode(); 
    } 
    
  • Verwenden Sie unterschiedliche verwenden könnte http Client wie OkHttp

+0

Cooler Mann, ich schwöre, ich sah ein anderes Verhalten, jedenfalls nur getestet Was Sie sagen und ist richtig +1 ... –

+0

Ich habe dieses Problem auf einem Android-Gerät, aber nicht ein anderes, ich denke, einige OEMs überschreiben die HttpUrlConnection-Klasse? Wie auch immer, diese Lösung hat funktioniert. – ashishduh

+0

Sie sind ein Gott unter den Menschen. –

1

Ich hatte das gleiche Problem auf Geräten mit Pre-KitKat Android, aber ich war mit der Volley-Bibliothek, so dass die Client-Seite von @ for3st für mich nicht funktioniert, musste ich es für Volley einstellen, hier ist es ist, hoffen, dass es jemanden kämpfen mit diesem Problem hilft:

HurlStack hurlStack = new HurlStack() { 
      @Override 
      public HttpResponse performRequest(final Request<?> request, final Map<String, String> additionalHeaders) throws IOException, AuthFailureError { 
       try { 
        return super.performRequest(request, additionalHeaders); 
       } catch (IOException e) { 
        return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 401, e.getMessage()); 
       } 
      } 
     }; 

Volley.newRequestQueue(context.getApplicationContext(), hurlStack); 

Fehler zurückgegeben wird und sie es erneut versuchen Politik kann es tun ist Aufgabe diese Weise wird eine 401 (zB Anfrage Token ... etc ...). Obwohl IOException von einem anderen Problem verursacht werden könnte, einem anderen als 401, können Sie die Ausnahmemeldung für das Autorisierungsschlüsselwort analysieren und einen anderen Antwortcode für andere zurückgeben.

1

Hatte das gleiche Problem auf einigen alten Geräten (Huawei Y330-U11 zum Beispiel). Korrekte Weise, es zu beheben, ist es, Server-Seite wie in der beliebtesten Antwort erwähnt zu beheben. Es ist wirklich enttäuschend, dass das Problem nur bei einigen Geräten auftritt. Und ich glaube, dass es aufgrund verschiedener Implementierungen von "UrlConnection" passiert. Verschiedene Android-Versionen - verschiedene "UrlConnection" -Implementierungen.

Also, Sie möchten es vielleicht reparieren, indem Sie die gleiche "UrlConnection" überall verwenden. Versuchen Sie, okhttp und okhttp-urlconnection zu verwenden.

Hier ist der Weg, um die Libs zu Ihrem gradle Build hinzuzufügen:

compile 'com.squareup.okhttp:okhttp:2.5.0' 
compile 'com.squareup.okhttp:okhttp-urlconnection:2.5.0' 

Es das Problem für mich an diesen veralteten Geräten gelöst. (Ich musste OkClient für Retrofit RestAdapter verwenden)

P.S. Neueste Androids zum Zeitpunkt des Schreibens verwenden die alte Version der OKHTTP-Bibliothek intern als "UrlConnection" -Implementierung (mit aktualisierten Paketnamen), so scheint es ziemlich solide Sache

Verwandte Themen