java.io.IOException : 未找到身份验证挑战
Posted
技术标签:
【中文标题】java.io.IOException : 未找到身份验证挑战【英文标题】:java.io.IOException : No authentication challenges found 【发布时间】:2013-06-11 20:29:54 【问题描述】:我是 android 新手,这是我在 android 上的第一个项目。我在“身份验证”问题上苦苦挣扎了一天多。我尝试了几种选择,但都没有奏效。
基本上,我想调用 REST API 并获得响应。我确信 API 没有问题,因为我在另一个 ios 应用程序中使用了相同的 API。
我通过了授权标头,但仍然进行身份验证,未显示任何找到的消息。我在 *** 上发现了一些与此相关的问题,但其中一些不起作用,而有些对我来说没有意义。
我得到状态码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 的截图:
任何帮助将不胜感激。谢谢。
【问题讨论】:
String authString = "用户名:密码";这个“用户名:密码”应该等于他们在网站开发者模型中的意思的字符串。例如:他们可能会这样使用 Loginid:password。 @Akash 你找到解决方案了吗? @AsafK 原因是 HttpUrlConnection 引发了状态码 400 及以上的异常。我们需要处理这个异常。就我而言,我最终使用了 HttpClient 而不是 HttpUrlConnetion,因为它可以处理所有状态代码。 @Akash 我得到的 401 是因为这是我在凭据错误的情况下发回的内容。我只是想知道错误消息(“未找到身份验证挑战。”),这对我来说似乎太笼统了。无论如何,根据您所说的,在我需要处理的情况下会发生 IOException 。谢谢。 你能解决它吗? 【参考方案1】:你可以使用这样的东西
catch (IOException ex)
if(ex.getMessage().toString().toLowerCase().equals(("No authentication challenges found").toLowerCase()))
// re-generate the authentication Token
【讨论】:
【参考方案2】:发生此错误是因为服务器发送了 401(未经授权)但没有给出 WWW-Authenticate
标头,这是对客户端下一步操作的提示。 WWW-Authenticate
标头告诉客户端需要哪种身份验证(Basic 或Digest)。这在无头 http 客户端中可能不是很有用,但这就是 HTTP 1.1 RFC is defined 的方式。发生错误是因为 lib 尝试解析 WWW-Authenticate
标头但不能。
来自 RFC:
(...)响应必须包含 WWW-Authenticate 头字段(部分 14.47) 包含适用于所请求资源的质询。(...)
如果您可以更改服务器,可能的解决方案:
添加一个虚假的“WWW-Authenticate”标头,例如:WWW-Authenticate: Basic realm="fake"
。这只是一种解决方法而不是解决方案,但它应该可以工作并且 http 客户端很满意 (see here a discussion of what you can put in the header)。但请注意,某些 http 客户端可能会自动重试请求,从而导致多个请求(例如,过于频繁地增加错误的登录计数)。这是在 iOS http 客户端中观察到的。
正如 loudvchar 在this blog 中提出的那样,为了避免对挑战的自动反应,例如浏览器中的弹出登录表单,您可以使用非标准身份验证方法,如下所示:@987654334 @。重要的是必须包含realm
。
使用 HTTP 状态代码 403
而不是 401
。它的语义不同,通常在使用登录 401 时是正确的响应 (see here for a detailed discussion),但在兼容性方面是更安全的解决方案。
如果您无法更改服务器,可能的解决方案:
正如@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();
使用不同的http客户端,如OkHttp
【讨论】:
帅哥,我发誓我看到了不同的行为,无论如何只是测试了你所说的并且是正确的 +1... 我在一台 Android 设备上遇到了这个问题,但在另一台设备上没有,我猜有些 OEM 覆盖了 HttpUrlConnection 类?无论哪种方式,此修复程序都有效。 你是男人中的神。【参考方案3】:在一些旧设备(例如华为 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 来改造 RestAdapter)
附:在撰写本文时,最新的 Android 在内部使用旧版本的 OKHTTP 库作为“UrlConnection”实现(带有更新的包名称),所以这似乎是相当可靠的事情
【讨论】:
【参考方案4】:我在运行 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 关键字解析异常消息,并为其他人返回不同的响应代码。
【讨论】:
【参考方案5】:您在哪个版本的 Android 上进行测试?
在 Gingerbread 的一些开发工作中,我在使用 Android 身份验证器时遇到了困难(我不知道它在更高版本的 Android 上的行为是否有所不同)。我使用 Fiddler2 检查我的应用程序和服务器之间的 HTTP 流量,发现身份验证器没有为每个 HTTP 请求发送身份验证字符串。我需要它。
相反,我使用了这个:
urlConnection.setRequestProperty("Authorization", "Basic " + Base64.encodeToString("userid:pwd".getBytes(), Base64.NO_WRAP ));
这是我的代码的剪切和粘贴。请注意,urlConnection 是一个 HttpURLConnection 对象。
【讨论】:
我试过这个。但没有奏效。甚至我也尝试过使用不同的 base64 标志,例如URL_SAFE
、NO_WRAP
、DEFAULT
。我也试过“URL_SAFE | NO_WRAP
”。
我检查了 Fiddler2 是否适用于 Windows。 MAC有这样的吗?
1) 在我的代码中,我删除了身份验证器(我没有尝试将它和 setRequestProperty 组合)2) 对于上面的 setRequestProperty,NO_WRAP 绝对是必要的,因为删除它会导致失败 3) 抱歉.我正在Windows上开发。我不知道有哪些 Mac 工具。
您的 iOS 应用程序是否使用 cookie?在我的 Android 应用中,我必须专门打开它。
不,它没有。我的问题和这个有关系吗?以上是关于java.io.IOException : 未找到身份验证挑战的主要内容,如果未能解决你的问题,请参考以下文章
android java.io.IOException:传输端点未连接
java.io.IOException:无法解密安全内容条目:javax.crypto.BadPaddingException:给定最终块未正确填充
IWAB0399E从WSDL生成Java时出错:java.io.IOException:类型{http://www.w3.org/2001/XMLSchema}array已引用但未定义