it-swarm.com.de

java.io.IOException: Keine Authentifizierungsherausforderungen gefunden

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:

logcat

Jede Hilfe wäre dankbar. Vielen Dank.

21
Geek

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 :

  • Fügen Sie einen gefälschten "WWW-Authenticate" -Header wie den folgenden hinzu: _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.
  • Wie von loudvchar in diesem Blog vorgeschlagen, um automatische Reaktionen auf die Herausforderung wie ein Popup-Anmeldeformular in einem Browser zu vermeiden, können Sie ein nicht Standardauthentifizierungsmethode wie folgt: _WWW-Authenticate: xBasic realm="fake"_. Der wichtige Punkt ist, dass das realm einbezogen werden muss.
  • Verwenden Sie den HTTP-Statuscode _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

47
patrickf

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.

1
Leo K

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

1
GregoryK

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.

1
UpLate

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
             }
0
Wowo Ot