Android: HttpsUrlConnection с Authenticator для базовой аутентификации повторяется навсегда, если пароль неверный (при ответе 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    

person Jens    schedule 18.10.2011    source источник
comment
возможный дубликат Android, HttpURLConnection и обработка неправильных учетных данных   -  person yanchenko    schedule 16.12.2012


Ответы (2)


Я решил эту проблему, абстрагировав логику запроса/ответа в класс 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());

            ...

    }
}
person jsadler    schedule 28.11.2012
comment
точно! Спасибо за указание на это! это соответствует закрытому ответу code.google.com/p/android /issues/detail?id=7058 Предполагается, что аутентификатор запрашивает у пользователя новый набор учетных данных, поскольку ранее предоставленные не работали. Если аутентификатор просто использует предварительно установленные учетные данные, ему необходимо отслеживать, что предыдущая попытка не удалась. - person Maks; 01.04.2014

Хотел бы я знать правильный ответ на этот вопрос, потому что столкнулся с точно такой же проблемой. Я не мог найти способ обработать ошибку аутентификации или даже получить уведомление об этом.

В итоге мне пришлось использовать 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()));
person nynexman4464    schedule 29.08.2012
comment
Хотя вроде неплохой вариант. Я ничего не менял в своем коде. Я только слежу, чтобы никто не менял пароль, не уведомив меня... :-) - person Jens; 30.08.2012