2013-06-15 26 views
20

Jestem początkującym użytkownikiem Androida i jest to mój pierwszy projekt na Androida. Zmagam się z problemem "uwierzytelniania" przez ponad jeden dzień. Próbowałem kilku opcji, ale żaden z nich nie działał.java.io.IOException: Nie znaleziono żadnych problemów z uwierzytelnianiem

Zasadniczo chcę wywołać interfejs API REST i uzyskać odpowiedź. Jestem pewien, że nie ma problemu z API, ponieważ używam tego samego w innej aplikacji na iOS.

Przepuszczam nagłówek autoryzacji, ale nadal uwierzytelnianie nie znaleziono komunikatu znalezionego. Znalazłem kilka pytań na temat stackoverflow związanych z tym, ale niektóre z nich nie działały, a niektóre nie mają dla mnie sensu.

Otrzymuję kod stanu 401. Wiem, że oznacza to, że żadne uwierzytelnienie nie zostało przekazane lub jeśli zostało przekazane, wtedy są one błędne. Jestem pewien, że moje zdane są poprawne.

Poniżej jest mój kod:

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


Spójrz na zrzut ekranu LogCat:

logcat

Każda pomoc będzie mile widziane. Dziękuję Ci.

+0

String authString = "username: password"; ta "nazwa użytkownika: hasło" powinna być równa ich ciągom, co mam na myśli w modelu programistów Witryny. na przykład: mogą używać Loginid: hasło w ten sposób. – manivannan

+0

@Akash czy znalazłeś rozwiązanie tego problemu? – AsafK

+0

@AsafK Powodem było to, że HttpUrlConnection podniósł wyjątek dla kodu stanu 400 i powyżej. Musimy poradzić sobie z tym wyjątkiem. W moim przypadku wykorzystałem HttpClient zamiast HttpUrlConnetion ze względu na obsługę wszystkich kodów statusu. – Geek

Odpowiedz

1

W jakiej wersji Androida przeprowadzasz testy?

Podczas pracy nad Gingerbread miałem problemy z Androidem authentator (nie wiem, czy zachowuje się inaczej w późniejszych wersjach Androida). Użyłem Fiddler2 do zbadania ruchu HTTP między moją aplikacją a serwerem, odkrywając, że wystawca uwierzytelnienia nie wysłał ciągu uwierzytelnienia dla każdego żądania HTTP. Potrzebowałem tego.

Zamiast tego uciekają się do tego:

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

Jest to cut-and-paste z mojego kodu. Zauważ, że urlConnection jest obiektem HttpURLConnection.

+0

Próbowałem tego. Ale nie działa. Nawet próbowałem używać różnych flag base64 takich jak 'URL_SAFE',' NO_WRAP', 'DEFAULT'. Próbowałem też "' URL_SAFE | NO_WRAP' ". – Geek

+0

Sprawdziłem, czy Fiddler2 jest dla Windows. Czy jest taki na MAC? – Geek

+0

Wersja Androida to ICS. – Geek

45

Ten błąd występuje, ponieważ serwer wysyła 401 (nieautoryzowane), ale nie daje nagłówka WWW-Authenticate, który jest wskazówką dla klienta, co dalej robić. Nagłówek WWW-Authenticate informuje klienta, jakiego rodzaju uwierzytelnienia jest potrzebne (Basic lub Digest). Prawdopodobnie nie jest to bardzo przydatne w przypadku bezgłowych klientów HTTP, ale tak właśnie jest w przypadku HTTP 1.1 RFC is defined. Błąd występuje, ponieważ lib próbuje przeanalizować nagłówek WWW-Authenticate, ale nie może.

Z RFC:

(...) Odpowiedź MUSI zawierać pole nagłówka uwierzytelniania w sieci WWW (sekcja 14,47) zawierającą wyzwanie zastosowanie do żądanego zasobu (...)

.

Możliwe rozwiązania jeśli może zmienić serwer:

  • dodać fake "uwierzytelniania w sieci WWW" nagłówek taki jak: WWW-Authenticate: Basic realm="fake". Jest to zwykłe rozwiązanie, a nie rozwiązanie, ale powinno działać, a klient HTTP jest zadowolony (see here a discussion of what you can put in the header).Ale uważaj, że niektórzy klienci http mogą automatycznie ponawiać żądanie, co skutkuje wieloma żądaniami (np. Zbyt często zwiększa liczbę błędnych danych logowania). Zaobserwowano to w przypadku klienta HTTP systemu iOS.
  • Zgodnie z propozycją loudvchar w this blog, aby uniknąć automatycznych reakcji na wyzwanie, takich jak wyskakujący formularz logowania w przeglądarce, można użyć niestandardowej metody uwierzytelniania, takiej jak: WWW-Authenticate: xBasic realm="fake". Ważne jest to, że należy dołączyć realm.
  • Użyj kodu stanu HTTP 403 zamiast 401. To semantyczne nie jest takie samo i zwykle podczas pracy z loginem 401 jest poprawna odpowiedź (see here for a detailed discussion), ale bezpieczniejsze rozwiązanie pod względem zgodności.

Możliwe rozwiązania jeśli nie zmiana serwera:

  • Jak @ErikZ napisał w swoim post można użyć spróbować & haczyk

    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(); 
    } 
    
  • Użyj innego klient http taki jak OkHttp

+0

Fajny człowiek, przysięgam, widziałem inne zachowanie, zresztą po prostu przetestowane co mówisz i czy jest poprawne +1 ... –

+0

Mam problem z jednym urządzeniem z systemem Android, ale nie z innym, domyślam się, że niektórzy producenci OEM nadpisują klasę HttpUrlConnection? Tak czy inaczej, ta poprawka działała. – ashishduh

+0

Jesteś bogiem między ludźmi. –

1

Miałem ten sam problem na urządzeniach z systemem Android, ale korzystałem z biblioteki Volley, więc poprawka po stronie klienta dostarczona przez @ for3st nie działała dla mnie, musiałem dostosować ją do Volley, tutaj to jest, nadzieję, że pomoże ktoś zmaga się z tym problemem:

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

ten sposób błędu 401 jest zwracany a polityka ponawiania może zrobić, że to zadanie (np Żeton żądania ... itd.). Chociaż wyjątek IOException może być spowodowany przez inny problem, inny niż 401, więc możesz zdecydować się na parsowanie wyjątku dla słowa kluczowego Authorization i zwrócić inny kod odpowiedzi dla innych.

1

Miał ten sam problem na niektórych starych urządzeniach (na przykład Huawei Y330-U11). Prawidłowy sposób naprawy polega na naprawieniu po stronie serwera, jak wspomniano w najpopularniejszej odpowiedzi.

Jednak to naprawdę rozczarowuje, że problem występuje tylko na urządzeniach niektórych. I wierzę, że dzieje się tak z powodu różnych implementacji "UrlConnection". Różne wersje Androida - różne implementacje "UrlConnection".

Można go naprawić, używając wszędzie tego samego "UrlConnection". Spróbuj użyć okhttp i okhttp-urlconnection.

Oto sposób dodać te bibliotekami do Gradle produkcji:

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

To rozwiązało problem dla mnie na tych przestarzałych urządzeń. (Musiałem użyć OkClient dla RestAdrit RestAdapter)

P.S. Najnowsze androidy w momencie pisania używają starej wersji biblioteki OKHTTP wewnętrznie jako impe- rementację "UrlConnection" (ze zaktualizowanymi nazwami pakietów), więc wydaje się być całkiem solidną rzeczą:

Powiązane problemy