java.io.IOException:未找到认证挑战

我是Android的新手,这是我在android上的第一个项目。 我在“认证”问题上挣扎了一天以上。 我尝试了几个选项,但都没有工作。

基本上,我想调用一个REST API并获得响应。 我相信在API中没有问题,因为我在另一个iOS应用程序中使用了相同的问题。

我通过了授权头,但仍然没有找到认证消息。 我发现在与此有关的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标头告诉客户端需要哪种认证(Basic或Digest)。 这对于无头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 。 它的语义是不一样的,通常当使用login 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之外,您可以选择解析授权关键字的异常消息,并为其他人返回不同的响应代码。

    链接地址: http://www.djcxy.com/p/71813.html

    上一篇: java.io.IOException : No authentication challenges found

    下一篇: How do I raise a Response Forbidden in django