java.io.IOException: 未发现身份验证质询

我是机器人的新手,这是我在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

任何帮助将不胜感激。谢谢。


答案 1

发生此错误的原因是服务器发送 401(未经授权),但没有提供 WWW-Authenticate 标头,这是对客户端下一步操作的提示。标头告诉客户端需要哪种类型的身份验证(基本摘要)。这在无外设http客户端中可能不是很有用,但这就是HTTP 1.1 RFC的定义。出现此错误的原因是库尝试分析标头,但无法解析。WWW-AuthenticateWWW-Authenticate

从 RFC:

(...)响应必须包含一个 WWW 身份验证标头字段(第 14.47 节),其中包含适用于所请求资源的质询。(...)

如果可以更改服务器,则可能的解决方案:

  • 添加一个伪造的“WWW-Authenticate”标头,如:。这只是一个解决方法,而不是一个解决方案,但它应该有效并且http客户端感到满意(请参阅此处的讨论,了解您可以在标头中放入什么)。但请注意,某些http客户端可能会自动重试请求,从而导致多个请求(例如,过于频繁地增加错误的登录计数)。这是在iOS http客户端上观察到的。WWW-Authenticate: Basic realm="fake"
  • 正如loudevchar在本博客中提出的,为了避免像浏览器中的弹出登录表单那样对质询做出自动反应,您可以使用非标准的身份验证方法,如下所示:。重要的一点是必须包括。WWW-Authenticate: xBasic realm="fake"realm
  • 使用 HTTP 状态代码而不是 。它的语义是不一样的,通常在使用login 401时是正确的响应(请参阅此处进行详细讨论),但在兼容性方面更安全的解决方案。403401

如果无法更改服务器,则可能的解决方案:

  • 正如@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


答案 2

您正在测试哪个版本的Android?

在Gingerbread的一些开发工作中,我在Android身份验证器上遇到了困难(我不知道它在更高版本的Android上的行为是否不同)。我使用 Fiddler2 检查我的应用和服务器之间的 HTTP 流量,发现身份验证器没有为每个 HTTP 请求发送身份验证字符串。我需要它。

相反,我求助于这个:

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

这是从我的代码中剪切和粘贴的。请注意,urlConnection 是一个 HttpURLConnection 对象。


推荐