Android:用于基本身份验证的身份验证器的HttpsUrlConnection在密码错误时永远迭代(在401响应中)

Jen*_*ens 11 android httpurlconnection http-status-code-401

HttpsUrlConnection通过使用Authenticator和设置默认Authenticator对象使用基本身份验证,如下所示:

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication("user", "userpass"
            .toCharArray());
    }
});
Run Code Online (Sandbox Code Playgroud)

当我访问我的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    
Run Code Online (Sandbox Code Playgroud)

jsa*_*ler 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());

            ...

    }
}
Run Code Online (Sandbox Code Playgroud)