簡體   English   中英

java.io.IOException:未找到身份驗證質詢

[英]java.io.IOException : No authentication challenges found

我是android的新手,這是我在android上的第一個項目。 我在“認證”問題上苦苦掙扎了一天多。 我嘗試了幾個選項,但沒有一個可行。

基本上,我想調用REST API並獲得響應。 我確信API中沒有問題,因為我在另一個iOS應用程序中使用相同的API。

我傳遞授權標題但仍然顯示身份驗證未找到消息。 我發現與stackoverflow相關的問題很少,但是其中一些沒有用,有些對我沒有意義。

我得到狀態代碼401 我知道這意味着要么沒有傳遞身份驗證,要么傳遞,那么它們就錯了。 在這里,我確信我傳遞的是正確的。

以下是我的代碼:

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


看一下logcat的截圖:

logcat的

任何幫助,將不勝感激。 謝謝。

發生此錯誤的原因是服務器發送401(未授權)但未提供WWW-Authenticate標頭,這是對客戶端下一步操作的提示。 WWW-Authenticate標頭告訴客戶端需要哪種身份驗證( 基本摘要 )。 這在無頭http客戶端中可能不是很有用,但這就是HTTP 1.1 RFC的定義方式 發生此錯誤是因為lib嘗試解析WWW-Authenticate標頭但不能。

來自RFC:

(...)響應必須包含WWW-Authenticate頭字段(第14.47節),其中包含適用於所請求資源的質詢。(...)

如果您可以更改服務器可能的解決方案:

  • 添加一個假的“WWW-Authenticate”標題,如: WWW-Authenticate: Basic realm="fake" 這僅僅是一種解決方法而不是解決方案,但它應該可以工作並且http客戶端是滿意的( 請參閱此處討論您可以在標題中添加的內容 )。 但請注意,某些http客戶端可能會自動重試請求,從而導致多個請求(例如,過於頻繁地增加錯誤的登錄計數)。 這是在iOS http客戶端上觀察到的。
  • 正如本博客中loudvchar提出的,為了避免像瀏覽器中的彈出式登錄表單一樣自動應對挑戰,您可以使用非標准的身份驗證方法,如下所示: WWW-Authenticate: xBasic realm="fake" 重要的是必須包含realm
  • 使用HTTP狀態代碼403而不是401 它的語義不一樣,通常在使用登錄401時是正確的響應( 請參閱此處進行詳細討論 ),但在兼容性方面更安全。

如果您無法更改服務器,可能的解決方案:

  • 正如@ErikZ在他的帖子中寫道,你可以使用try&catch

     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(); } 
  • 使用不同的http客戶端,如OkHttp

您正在測試哪個版本的Android?

在Gingerbread的一些開發工作中我遇到了Android身份驗證器的困難(我不知道它在Android的更高版本中是否表現不同)。 我使用Fiddler2檢查我的應用程序和服務器之間的HTTP流量,發現身份驗證器沒有為每個HTTP請求發送身份驗證字符串。 我需要它。

相反,我訴諸於此:

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

這是我的代碼中的剪切和粘貼。 請注意,urlConnection是一個HttpURLConnection對象。

我在運行pre-KitKat Android的設備上遇到了同樣的問題,但我使用的是Volley庫,所以@ for3st提供的客戶端修復對我來說不起作用,我不得不為Volley調整它,在這里,希望它幫助有人解決這個問題:

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

這樣就會返回401錯誤,您的重試策略可以完成它的工作(例如請求令牌......等等)。 雖然IOException可能是由其他問題引起的,但除了401之外,您可以選擇解析Authorization關鍵字的異常消息,並為其他人返回不同的響應代碼。

在某些舊設備上有同樣的問題(例如華為Y330-U11)。 解決問題的正確方法是修復服務器端,如最受歡迎的答案中所述。

然而,這個問題僅在某些設備上發生,這真的令人失望。 我相信它是由於“UrlConnection”的不同實現而發生的。 不同的Android版本 - 不同的“UrlConnection”實現。

因此,您可能希望通過在任何地方使用相同的“UrlConnection”來修復它。 嘗試使用okhttp和okhttp-urlconnection。

以下是將這些庫添加到gradle構建中的方法:

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

它為那些過時的設備解決了我的問題。 (我不得不使用OkClient進行Retrofit RestAdapter)

PS最新的機器人在編寫時使用舊版本的OKHTTP庫作為“UrlConnection”實現(具有更新的軟件包名稱),所以它似乎是相當堅固的東西

你可以使用這樣的東西

catch (IOException ex) {
        if(ex.getMessage().toString().toLowerCase().equals(("No authentication challenges found").toLowerCase()))
// re-generate the authentication Token
             }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM