Android: HttpsUrlConnection с аутентификатором для базовой проверки подлинности итерации навсегда, когда пароль неверен (на ответ 401)

Я использую 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    

Ответ 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());

            ...

    }
}

Ответ 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()));