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 响应中)的主要内容,如果未能解决你的问题,请参考以下文章

php:如何重定向到带有错误消息的同一页面

包含带有数据库连接错误的 php 文件

Android - MD之TextInputLayout的使用

带有 Kotlin 的 Android 中的 HTTP 请求

通过带有指纹的 android 密钥库检索用户凭据(用户名和密码)

使用 MFC 6 的带有密码的管理员登录页面? [关闭]