web-dev-qa-db-ja.com

java.io.IOException:認証チャレンジが見つかりません

私はAndroidに初心者です。これはAndroidでの最初のプロジェクトです。1日以上「認証」問題に苦労しています。いくつかのオプションを試しましたが、どれも機能しませんでした。

基本的に、REST APIを呼び出して応答を取得します。別のiOSアプリケーションで同じAPIを使用するので、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

任意の助けをいただければ幸いです。ありがとうございました。

22
Geek

このエラーは、サーバーが401(Unauthorized)を送信しますが、クライアントに次に何をすべきかのヒントとなる WWW-Authenticate ヘッダーを送信しないために発生します。 WWW-Authenticateヘッダーは、必要な認証の種類( Basic または Digest )をクライアントに伝えます。これはおそらく、ヘッドレスhttpクライアントではあまり有用ではありませんが、 HTTP 1.1 RFCの定義 です。このエラーは、libがWWW-Authenticateヘッダーを解析しようとしますが、解析できないために発生します。

RFCから:

(...)応答には、要求されたリソースに適用可能なチャレンジを含むWWW-Authenticateヘッダーフィールド(セクション14.47)を含める必要があります。(...)

サーバーを変更することができる場合の解決策:

  • WWW-Authenticate: Basic realm="fake"のような偽の「WWW-Authenticate」ヘッダーを追加します。これは単なる回避策であり、解決策ではありませんが、動作するはずであり、httpクライアントは満足しています( header に入れることができるものについての議論を参照)。ただし、一部のhttpクライアントは自動的にリクエストを再試行し、複数のリクエストが発生する場合があることに注意してください(たとえば、間違ったログインカウントを頻繁にインクリメントします)。これは、iOS httpクライアントで観察されました。
  • this blogloudvcharで提案されているように、ブラウザのポップアップログインフォームのようなチャレンジに対する自動反応を避けるために、 WWW-Authenticate: xBasic realm="fake"のような非標準の認証方法を使用します。重要な点は、realmを含める必要があるということです。
  • 403の代わりにHTTPステータスコード401を使用します。セマンティックは同じではなく、通常、ログイン401で作業する場合は正しい応答です( 詳細な議論についてはこちらをご覧ください )が、互換性に関してはより安全なソリューションです。

サーバーを変更できない場合に可能な解決策:

  • @ErikZが post で書いたように、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(); 
    }
    
  • OkHttp のような別のhttpクライアントを使用します

48
patrickf

KitKat以前のAndroidを実行しているデバイスでも同じ問題が発生しましたが、Volleyライブラリを使用していたため、@ for3stが提供するクライアント側の修正が機能しませんでした。この問題に苦しんでいる人を助ける:

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キーワードの例外メッセージの解析を選択し、他の問題に対して異なる応答コードを返すことができます。

1
Leo K

いくつかの古いデバイス(Huawei Y330-U11など)でも同じ問題が発生していました。それを修正する正しい方法は、最も一般的な回答で述べられているようにサーバー側で修正することです。

ただし、この問題がsomeデバイスでのみ発生することは本当に残念です。そして、「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'

それらの古いデバイスで私のために問題を解決しました。 (レトロフィットRestAdapterにはOkClientを使用する必要がありました)

追伸執筆時点の最新のAndroidは、内部的に「UrlConnection」実装として古いバージョンのOKHTTPライブラリ(更新されたパッケージ名を使用)を使用しているため、非常に堅実なもののようです。

1
GregoryK

Android=のどのバージョンでテストしていますか?

Gingerbreadの開発作業中にAndroidオーセンティケーターに問題がありました(Androidの以降のバージョンで動作が異なるかどうかはわかりません)。Fiddler2を使用してアプリ間のHTTPトラフィックを調べました。サーバーは、オーセンティケーターがすべてのHTTPリクエストに対して認証文字列を送信しなかったことを発見しました。

代わりに、私はこれに頼った:

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

それは私のコードからのカットアンドペーストです。 urlConnectionはHttpURLConnectionオブジェクトであることに注意してください。

1
UpLate

このようなものを使用できます

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