Ich bin ein Neuling in Android und dies ist mein erstes Projekt auf Android. Ich kämpfe seit mehr als einem Tag mit dem Authentifizierungsproblem. Ich habe mehrere Optionen ausprobiert, aber keine davon hat funktioniert.
Grundsätzlich möchte ich eine REST - API aufrufen und eine Antwort erhalten. Ich bin sicher, dass es in der API kein Problem gibt, da ich dasselbe in einer anderen iOS-Anwendung verwende.
Ich übergeben den Autorisierungsheader, aber es wird keine Authentifizierungsnachricht angezeigt. Ich habe zu diesem Thema nur wenige Fragen zu stackoverflow gefunden, aber einige von ihnen haben nicht funktioniert und manche machen für mich keinen Sinn.
Ich bekomme den Statuscode 401
. Ich weiß, das bedeutet entweder keine Authentifizierung oder wenn bestanden, dann sind sie falsch. Ich bin mir sicher, dass meine Bestanden richtig 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();
}
Siehe Screenshot von logcat:
Jede Hilfe wäre dankbar. Vielen Dank.
Dieser Fehler tritt auf, weil der Server eine 401 (nicht autorisiert) sendet, aber keinen WWW-Authenticate
-Header ausgibt, der dem Client einen Hinweis darauf gibt, was als Nächstes zu tun ist. Der _WWW-Authenticate
_ -Header teilt dem Client mit, welche Art von Authentifizierung erforderlich ist (entweder Basic oder Digest ). Dies ist bei Headless-HTTP-Clients wahrscheinlich nicht sehr nützlich, aber so wird der HTTP 1.1 RFC definiert. Der Fehler tritt auf, weil die Bibliothek versucht, den Header _WWW-Authenticate
_ zu analysieren, dies jedoch nicht kann.
Aus dem RFC:
(...) Die Antwort MUSS ein WWW-Authenticate-Headerfeld (Abschnitt 14.47) enthalten, das eine Herausforderung für die angeforderte Ressource enthält. (...)
Mögliche Lösungen, wenn Sie den Server ändern können :
WWW-Authenticate: Basic realm="fake"
_. Dies ist nur eine Problemumgehung, keine Lösung, aber sie sollte funktionieren und der http-Client ist zufrieden ( siehe hier eine Diskussion darüber, was Sie in den Header einfügen können ). Beachten Sie jedoch, dass einige http-Clients die Anforderung möglicherweise automatisch wiederholen, was zu mehreren Anforderungen führt (z. B. wird die Anzahl der falschen Anmeldungen zu oft erhöht). Dies wurde mit dem iOS-http-Client beobachtet.WWW-Authenticate: xBasic realm="fake"
_. Der wichtige Punkt ist, dass das realm
einbezogen werden muss.403
_ anstelle von _401
_. Die Semantik ist nicht dieselbe und normalerweise ist die Arbeit mit Login 401 eine korrekte Antwort ( siehe hier für eine ausführliche Diskussion ), aber die sicherere Lösung in Bezug auf Kompatibilität.Mögliche Lösungen, wenn Sie den Server nicht ändern können :
Wie @ErikZ in seinem post schrieb, könnten Sie einen Versuch & Fang verwenden
_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 einen anderen http-Client wie OkHttp
Ich hatte das gleiche Problem auf Geräten, die vor KitKat Android ausgeführt wurden, aber ich verwendete die Volley-Bibliothek, sodass der von @ for3st bereitgestellte clientseitige Fix für mich nicht funktionierte. Ich musste ihn für Volley anpassen hilft jemandem, der mit diesem Problem zu kämpfen hat:
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);
Auf diese Weise wird ein 401-Fehler zurückgegeben, und Ihre Wiederholungsrichtlinie kann ihren Job ausführen (z. B. Anfragetoken usw.). Obwohl IOException von einem anderen Problem als einem 401 verursacht werden kann, können Sie die Ausnahmemeldung für das Autorisierungsschlüsselwort analysieren und einen anderen Antwortcode für andere zurückgeben.
Hatte das gleiche Problem auf einigen alten Geräten (beispielsweise Huawei Y330-U11). Der richtige Weg, um das Problem zu beheben, besteht darin, das Problem auf der Serverseite zu beheben.
Es ist jedoch wirklich enttäuschend, dass das Problem nur bei einigen Geräten auftritt. Und ich glaube, dass dies auf unterschiedliche Implementierungen von "UrlConnection" zurückzuführen ist. Verschiedene Android-Versionen - verschiedene "UrlConnection" -Implementierungen.
Vielleicht möchten Sie das Problem beheben, indem Sie überall dieselbe "UrlConnection" verwenden. Versuchen Sie, okhttp und okhttp-urlconnection zu verwenden.
So fügen Sie diese Bibliotheken zu Ihrem Gradle-Build hinzu:
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.5.0'
Es löste das Problem für mich auf diesen veralteten Geräten. (Ich musste OkClient für Retrofit RestAdapter verwenden)
P.S. Die neuesten Androids verwenden zum Zeitpunkt des Schreibens die alte Version der OKHTTP-Bibliothek intern als "UrlConnection" -Implementierung (mit aktualisierten Paketnamen), daher scheint es eine ziemlich solide Sache zu sein
Mit welcher Android-Version testen Sie?
Ich hatte Probleme mit dem Android-Authentifikator während einiger Entwicklungsarbeiten an Gingerbread (ich weiß nicht, ob es sich in späteren Versionen von Android anders verhält). Ich habe Fiddler2 verwendet, um den HTTP-Verkehr zwischen meiner App und dem Server zu untersuchen. Dabei stellte ich fest, dass der Authentifikator die Authentifizierungszeichenfolge nicht für jede HTTP-Anforderung gesendet hat. Ich brauchte es zu.
Stattdessen habe ich darauf 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.
sie können so etwas verwenden
catch (IOException ex) {
if(ex.getMessage().toString().toLowerCase().equals(("No authentication challenges found").toLowerCase()))
// re-generate the authentication Token
}