Android:当密码错误时,带有 Authenticator 的 HttpsUrlConnection 将永远迭代(在 401 响应中)
Posted
技术标签:
【中文标题】Android:当密码错误时,带有 Authenticator 的 HttpsUrlConnection 将永远迭代(在 401 响应中)【英文标题】:Android: HttpsUrlConnection with Authenticator for Basic Authentication iterates forever when password is wrong (on 401 response) 【发布时间】:2011-12-10 03:12:23 【问题描述】:我正在使用HttpsUrlConnection
和基本身份验证,方法是使用Authenticator
并设置默认的Authenticator
对象,如下所示:
Authenticator.setDefault(new Authenticator()
protected PasswordAuthentication getPasswordAuthentication()
return new PasswordAuthentication("user", "userpass"
.toCharArray());
);
当我访问我的网络服务时,连接调用我的getPasswordAuthentication()
方法来获取凭据并将其发送到网络服务器。只要密码正确,就可以正常工作。 :)
但是,恰好有人更改了网络服务器上的基本身份验证密码,然后我的请求没有返回。
我调试了它,结果是我对getInputStream()
的调用永远不会返回。 HttpsUrlConnection
确实得到了 401 响应,并通过再次获取相同的凭据在内部对此作出反应。但由于我只提供了一个用户和密码,这将再次失败(并且再次......)。
所以我的问题是:我怎样才能防止这种情况发生?哪里有一个钩子可以对错误的密码做出反应(分别是 401 响应),以便我可以显示适当的错误消息并取消请求?
这是在HttpsUrlConnection
上重复调用的方法的堆栈跟踪的摘录:
1: MyOwnHttpConnection$3.getPasswordAuthentication() line: 99
2: Authenticator.requestPasswordAuthentication(InetAddress, int, String, String, String) line: 162
3: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).getAuthorizationCredentials(String) line: 1205
4: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).processAuthHeader(String, String) line: 1178
5: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).processResponseHeaders() line: 1118
6: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).retrieveResponse() line: 1044
7: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).getInputStream() line: 523
8: HttpsURLConnectionImpl.getInputStream() line: 283
【问题讨论】:
android, HttpURLConnection and handling bad credentials 的可能重复项 【参考方案1】:我通过将请求/响应逻辑抽象到 MyRequest
类中解决了这个问题。这允许我拥有一个请求范围的变量,它可以告诉我的Authenticator
是否应该使用指定的用户名和密码发出请求,或者是否应该停止重试(通过返回null
)。它看起来有点像下面(考虑这个伪代码)
public class MyRequest
private boolean alreadyTriedAuthenticating = false;
private URL url;
...
public void send()
HttpUrlConnection connection = (HttpUrlConnection) url.openConnection();
Authenticator.setDefault(new Authenticator()
protected PasswordAuthentication getPasswordAuthentication()
if (!alreadyTriedAuthenticating)
alreadyTriedAuthenticating = true;
return new PasswordAuthentication(username, password.toCharArray());
else
return null;
InputStream in = new BufferedInputStream(connection.getInputStream());
...
【讨论】:
没错!感谢您指出了这一点!根据关闭code.google.com/p/android/issues/detail?id=7058 的答案,Authenticator 应该要求用户提供一组 new 凭据,因为之前提供的凭据不起作用。如果身份验证器只是使用预设的凭据,那么它需要跟踪它之前的尝试失败。【参考方案2】:我希望我知道正确的答案,因为我遇到了完全相同的问题。我找不到处理身份验证错误的方法,甚至找不到相关通知。
我最终不得不改用HttpClient
。
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(loginUrl);
String authString = (userName+":"+password);
get.addHeader("Authorization", "Basic " +
Base64.encodeToString(authString.getBytes(),Base64.NO_WRAP));
HttpResponse response = client.execute(get);
BufferedReader r = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
【讨论】:
似乎是一个不错的选择。我没有更改我的代码中的任何内容。我只确保没有人在不通知我的情况下更改密码... :-)以上是关于Android:当密码错误时,带有 Authenticator 的 HttpsUrlConnection 将永远迭代(在 401 响应中)的主要内容,如果未能解决你的问题,请参考以下文章
Android - MD之TextInputLayout的使用
带有 Kotlin 的 Android 中的 HTTP 请求