HTTPClient отправляет два запроса при использовании Basic Auth?

Я использую HTTPClient версии 4.1.2, чтобы попытаться получить доступ к REST через HTTP API, для которого требуется базовая аутентификация. Вот код клиента:

DefaultHttpClient httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager());
// Enable HTTP Basic Auth
httpClient.getCredentialsProvider().setCredentials(
    new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), 
    new UsernamePasswordCredentials(this.username, this.password));

HttpHost proxy = new HttpHost(this.proxyURI.getHost(), this.proxyURI.getPort());

httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, proxy);

Когда я создаю запрос POST, например:

HttpPost request = new HttpPost("http://my/url");
request.addHeader(new BasicHeader("Content-type", "application/atom+xml; type=entry")); // required by vendor
request.setEntity(new StringEntity("My content"));

HttpResponse response = client.execute(request);

Я вижу в Charles Proxy, что отправляются два запроса. Один без заголовка Authorization: Basic ... и один с ним с. Первый терпит неудачу с 401, как и следовало ожидать, но второй проходит отлично с 201.

кто-нибудь знает, почему это произошло? Спасибо!

ИЗМЕНИТЬ:

Я должен пояснить, что уже просмотрел этот вопрос , но, как видите, я установил AuthScope таким же образом, и это не решило мою проблему. Кроме того, я создаю новый HttpClient каждый раз, когда делаю запрос (хотя я использую один и тот же ConnectionManager), но даже если я использую один и тот же HttpClient для нескольких запросов, проблема все еще сохраняется.

РЕДАКТИРОВАТЬ 2:

Таким образом, похоже, что @LastCoder предложил способ сделать это. См. этот ответ на другой вопрос. Проблема связана с отсутствием у меня знаний о спецификации HTTP. То, что я хочу сделать, называется «превентивной аутентификацией» и HttpClient документы упоминают это здесь. К счастью, ответ, указанный выше, намного короче и чище.


person daveslab    schedule 02.03.2012    source источник
comment
Я заметил, что то же самое происходит при использовании soap-ui, хотя учетные данные были указаны.   -  person Harindaka    schedule 02.03.2012
comment
Мне интересно, действительно ли это нормальное поведение. Клиент делает http-запрос, не предполагая никакой аутентификации, а затем ему сообщают (через 401), что требуется базовая аутентификация. Теоретически базовая аутентификация может быть выполнена упреждающе, но другие схемы аутентификации (например, дайджест) требуют дополнительного согласования.   -  person seand    schedule 02.03.2012
comment
@seand Вы знаете, я действительно думал об этом, но я не знаю, встроено ли это в протокол HTTP или что-то в этом роде.   -  person daveslab    schedule 02.03.2012
comment
@devslab HTTP-клиент может отправить базовый заголовок аутентификации заранее (@LastCoder упоминает, как это сделать). Я думаю, что HttClient не знал, что серверу нужна базовая аутентификация. Вы сказали ему кредиты вперед, но не протокол. Если есть способ сказать, что это базовая аутентификация, это может сократить лишний прыжок.   -  person seand    schedule 02.03.2012
comment
Оказывается, @LastCoder обнаружил, что это часть спецификации HTTP.   -  person daveslab    schedule 03.03.2012


Ответы (2)


Вместо использования .setCredentials() почему бы вам просто не закодировать USERNAME:PASSWORD и добавить заголовок аутентификации с помощью .addHeader()

person Louis Ricci    schedule 02.03.2012
comment
Я мог бы сделать это, конечно, и я проверю, имеет ли это значение, но в соответствии с примером кода (bit. ly/wEsEhY) в репозитории HttpClient я должен это сделать. - person daveslab; 02.03.2012
comment
@daveslab - это часть спецификации http-клиента, чтобы сначала запросить ресурс анонимно и ответить на 401 с заголовком авторизации. Если бы это было не так, клиенты рассылали бы веб-серверам спам с учетными данными заголовка авторизации, которые веб-серверу даже не нужны. Это просто лучшая практика безопасности. - person Louis Ricci; 02.03.2012
comment
Это решение грубой силы, но при необходимости будет сложнее переключать протоколы (например, если сервер изменился на использование дайджест-аутентификации) - person seand; 02.03.2012
comment
Комментарий @LastCoder ЯВЛЯЕТСЯ ответом на этот вопрос - person Juan Campa; 21.01.2013

Это означает, что ваш сервер/целевая конечная точка создает новый сеанс для каждого клиентского запроса. Это заставляет каждый ваш запрос проходить через рукопожатие, что означает, что клиенты сначала делают вызов и понимают, что ему нужна авторизация, а затем следует авторизация. Что вам нужно сделать, это предварительно отправить авторизацию следующим образом:

httpClient.getParams().setAuthenticationPreemptive(true);

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

person eadjei    schedule 26.02.2013