2012-03-02 5 views
12

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

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.

Кто-нибудь знает, почему это происходит? Благодаря!

EDIT:

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

EDIT 2:

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

+1

Я заметил то же самое, что и при использовании soap-ui, хотя были указаны учетные данные. – Harindaka

+0

Мне интересно, действительно ли это нормальное поведение. Клиент делает HTTP-запрос, не предполагая какого-либо авторизации, и затем сообщается (через 401), что необходим базовый auth. Теоретически базовый auth может быть выполнен превентивно, но другие схемы аутентификации (например, дайджест) нуждаются в дополнительных переговорах – seand

+0

@seand. Вы знаете, что я действительно это рассматривал, но я не знаю, встроено ли это в протокол HTTP или что-то в этом роде. – daveslab

ответ

10

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

+0

Я мог бы это сделать, конечно, и я проверю, не изменилось ли это, но в соответствии с примером кода (http://bit.ly/wEsEhY) в репозитории «HttpClient» это так, как я должен сделать Это. – daveslab

+7

@ daveslab - это часть спецификации http-клиента, чтобы сначала запросить ресурс анонимно и ответить на 401 с заголовком авторизации. Если бы не клиенты, были бы спам-серверами с учетными данными заголовка авторизации, веб-сервер даже не нужен. Это просто лучшая практика безопасности. –

+0

Это решение грубой силы, но было бы затруднительно переключать протоколы, если это необходимо (например, если сервер был изменен для использования дайджеста auth) – seand

2

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

httpClient.getParams(). SetAuthenticationPreemptive (true);

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