HttpURLConnection在Android 2.x中正常工作,但在4.1中出现问题:找不到身份验证挑战。

30

我有一些典型的代码,使用HttpURLConnection获取带有URL的文件。 它们在Android 1.x和2.x中运行良好。但在Android 4.1中失败了!

我在网上搜索了一下,但发现很少有类似的信息。 请有人帮忙调查这个问题吗?

private String mURLStr; 
private HttpURLConnection mHttpConnection;

...

url = new URL(mURLStr);

...

mHttpConnection = (HttpURLConnection) url.openConnection();
mHttpConnection.setDoOutput(true);
mHttpConnection.setRequestMethod("GET");

...

InputStream is = mHttpConnection.getInputStream();

getInputStream方法会抛出一个异常:

08-01 15:56:48.856: W/System.err(13613): java.io.IOException: No authentication challenges found
08-01 15:56:48.856: W/System.err(13613):      at libcore.net.http.HttpURLConnectionImpl.getAuthorizationCredentials(HttpURLConnectionImpl.java:427)
08-01 15:56:48.866: W/System.err(13613):      at libcore.net.http.HttpURLConnectionImpl.processAuthHeader(HttpURLConnectionImpl.java:407)
08-01 15:56:48.866: W/System.err(13613):      at libcore.net.http.HttpURLConnectionImpl.processResponseHeaders(HttpURLConnectionImpl.java:356)
08-01 15:56:48.866: W/System.err(13613):      at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:292)
08-01 15:56:48.866: W/System.err(13613):      at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168)
...

1
我删除了“mHttpConnection.setDoOutput(true)”这一行后,它就正常工作了!看起来这个问题和其他数百万个问题的原因相同:如果设置setDoOutput(true),Android 4.x会将http GET转换为POST![供您参考:http://webdiary.com/tag/java-net-httpurlconnection/] - firebear
我也遇到了同样的问题,但我没有使用 setDoOutput()。在 Android 4.0 Ice Cream Sandwich (ICS) 上一切正常,但在 Android 4.1 Jelly Bean (JB) 上出现了问题。顺便说一下,setDoOutput() 将请求转换为 POST 请求是完全有道理的,因为 GET 请求不应该在正文中有负载。 - cimnine
9个回答

27

我目前正在遇到同样的问题。在4.1 Jelly Bean上,当调用HttpURLConnection的getResponseCode()方法时,会出现IOException异常,显示“未找到身份验证挑战”。

我已经在网上搜索了Android源代码的更改,发现以下情况:

4.0.4(正常工作):https://bitbucket.org/seandroid/libcore/src/7ecbe081ec95/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java

4.1.1(不起作用):https://bitbucket.org/seandroid/libcore/src/6b27266a2856/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java

正如人们在4.1 JB中所看到的那样,getAuthorizationCredentials()方法会抛出IOException异常。它使用HeaderParser.parseChallenges(..)解析响应中找到的质询头,如果返回的列表为空,则抛出异常,前提是响应代码是401或407。

https://bitbucket.org/seandroid/libcore/src/6b27266a2856/luni/src/main/java/libcore/net/http/HeaderParser.java

我们正在调查是什么导致了列表为空,但怀疑我们的服务器可能在挑战头中使用realm=...而不是realm="..."。缺少引号可能是这个问题的原因。我们必须进一步调查是否确实如此,并确定是否可以解决它。


1
对于清晰的解释和代码链接,点赞。代码还期望realm="成为方案后的第一个参数。 - J_D
1
到目前为止,我们已经能够验证在没有引号的情况下使用realm=确实是导致我们出现错误的原因。我们已经能够在后端进行更改,现在一切都按预期运行。 - Hendrik
2
我最终通过删除 mHttpConnection.setDoOutput(true); 让我的应用在 Android 4.x 下工作了。但是我仍然不清楚根本原因。 - firebear
优秀的回答,加一分。我的问题在于服务器返回的是realm=...而不是realm="..."。修复后,应用程序在ICS和JB上都能正常运行。 - SysHex
你找到解决方法了吗?它在KitKat上能用,但在JB上不能。 - Blackbelt

26
根据RFC2617,401(未授权)响应消息由源服务器用于挑战用户代理的授权。此响应必须包含一个WWW-Authenticate头字段,其中至少包含一个适用于所请求资源的挑战。
在Android中,当服务器返回401未经授权或407需要代理身份验证状态码而没有设置WWW-Authenticate头时,HttpURLConnection getResponseCode()方法会抛出“java.io.IOException:未找到身份验证挑战”的异常。
如果您拥有服务器端API,则可以通过在返回401或407时添加所需的WWW-Authenticate标头来修复它。在我的情况下,我使用PHP进行了如下修复:
header('WWW-Authenticate: OAuth realm="users"');
header('HTTP/1.1 401 Unauthorized');

6

我也遇到了同样的问题。我找到了一个解决办法,但是在Android 2上无法实现。在Jelly Bean上,这个方法可以正常工作。只需要使用getErrorStream()代替getInputStream()。

try
{
    responseStream = new BufferedInputStream(connection.getInputStream());
}
catch(IOException e)
{
    responseStream = new BufferedInputStream(connection.getErrorStream());
}

1

标题

我已经为果冻豆解决了问题。请使用以下代码来应对上述情况。

DefaultHttpClient client = new DefaultHttpClient();
client.getCredentialsProvider().setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials(userName,userPass));
HttpGet request = new HttpGet();
request.addHeader("Accept", "application/xml");
request.setURI(new URI(service));
HttpResponse response = client.execute(request);

你得到了你需要的正确响应。

0
我在一个需要使用 cookie 正确运行的 web 服务中遇到了类似的问题。显然,与之前的版本不同,Jelly Bean 没有默认创建 cookie 存储。因此,该服务无法找到我的会话并且每次尝试访问该服务时都会抛出 401 错误。将以下代码添加到我的应用程序初始化中解决了这个问题:
// enable VM-wide cookie support for HttpUrlConnection
// see http://developer.android.com/reference/java/net/HttpURLConnection.html for details
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);

0

检查您的服务器是否返回了错误401-未经授权。我相信Android代码看到该响应并认为它是提供身份验证详细信息的意图。在我的情况下,我只是向我的服务器提供了错误的令牌。


0

0

有一个解决方案

在你的代码中删除这个

HttpConnection.setDoOutput(true);

它将在 ICSJelly Bean 上运行


0
我用的一个解决方案(我正在使用Android的Volley库)是使用{{link1:Square的OkHttp库}}。他们的实现正确处理此问题并按预期返回401。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接