Android:当密码错误时(在401响应时),使用Authenticator进行基本身份验证的HttpsUrlConnection会无限迭代。

11

我正在使用HttpsUrlConnection进行基本身份验证,方法是使用Authenticator并设置一个默认的Authenticator对象,代码如下:

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication("user", "userpass"
            .toCharArray());
    }
});
当我访问我的 Web 服务时,连接调用我的 getPasswordAuthentication() 方法获取凭据并将其发送到 Web 服务器。只要密码正确,这就可以正常工作。 :)
但是,有人更改了 Web 服务器上的基本身份验证密码,然后我的请求就没有返回。
我进行了调试,发现我的调用 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    

1
可能是重复的问题:Android、HttpURLConnection和处理错误凭据 - yanchenko
2个回答

5
我通过将请求/响应逻辑抽象为一个名为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());

            ...

    }
}

没错!感谢您指出这一点!这符合关闭的答案http://code.google.com/p/android/issues/detail?id=7058。认证器应该要求用户提供*新*的凭据,因为之前提供的凭据不起作用了。如果认证器只是使用预设的凭据,则需要跟踪上一次尝试失败的情况。 - Maks

3
我希望我能给出正确的答案,因为我遇到了完全相同的问题。我找不到处理身份验证错误的方法,甚至无法收到通知。
最终我必须使用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()));

似乎是一种很好的替代方案。我并没有在我的代码中做出任何更改。我只是确保没有人在未通知我情况下更改密码... :-) - Jens

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