Учетные данные HTTP-клиента Apache 4.3 на запрос

Я просматривал пример дайджест-аутентификации по адресу:

http://hc.apache.org/httpcomponents-client-4.3.x/examples.html

В моем сценарии есть несколько потоков, выдающих HTTP-запросы, и каждый из них должен быть аутентифицирован с использованием собственного набора учетных данных. Кроме того, учтите, что этот вопрос, вероятно, очень специфичен для HTTP-клиента Apache версии 4.3 и выше, 4.2 обрабатывает аутентификацию, вероятно, по-другому, хотя я сам не проверял. Тем не менее, вот собственно вопрос.

Я хочу использовать только один экземпляр клиента (статический член класса, то есть потокобезопасный) и предоставить ему диспетчер соединений для поддержки нескольких одновременных запросов. Дело в том, что каждый запрос будет предоставлять разные учетные данные, и я не вижу способа назначать учетные данные для каждого запроса, поскольку поставщик учетных данных устанавливается при создании http-клиента. Из ссылки выше:

[...]

    HttpHost targetHost = new HttpHost("localhost", 80, "http");
    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(
            new AuthScope(targetHost.getHostName(), targetHost.getPort()),
            new UsernamePasswordCredentials("username", "password"));
    CloseableHttpClient httpclient = HttpClients.custom()
            .setDefaultCredentialsProvider(credsProvider).build();

[...]

Проверка:

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html#d5e600

Пример кода в пункте 4.4 (ищите 4.4. HTTP-аутентификация и контекст выполнения), по-видимому, говорит о том, что HttpClientContext получает кэш аутентификации и поставщика учетных данных, а затем передается в HTTP-запрос. Рядом выполняется запрос, и кажется, что клиент получит учетные данные, фильтруемые хостом в HTTP-запросе. Другими словами: если контекст (или кеш) имеет действительные учетные данные для целевого хоста текущего HTTP-запроса, он будет использовать их. Проблема для меня в том, что разные потоки будут выполнять разные запросы к одному и тому же хосту.

Есть ли способ предоставить пользовательские учетные данные для каждого HTTP-запроса?

Спасибо заранее за ваше время! :)


person Francisco Carriedo Scher    schedule 08.10.2013    source источник
comment
Возможный дубликат http://stackoverflow.com/questions/2516345/authenticating-a-single-request-with-httpclient-4-x   -  person davidwebster48    schedule 04.01.2016


Ответы (2)


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

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

CloseableHttpClient httpclient = HttpClients.createDefault();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("user:pass"));
HttpClientContext localContext = HttpClientContext.create();
localContext.setCredentialsProvider(credentialsProvider);

HttpGet httpget = new HttpGet("http://localhost/");

CloseableHttpResponse response = httpclient.execute(httpget, localContext);
try {
    EntityUtils.consume(response.getEntity());
} finally {
    response.close();
}
person ok2c    schedule 08.10.2013
comment
Извините, мой вопрос был неточным: Проблема для меня в том, что разные потоки с разными учетными данными будут выполнять запросы к одному и тому же хосту. Итак, имея только одного клиента и только один экземпляр поставщика учетных данных для всех потоков (каждый поток будет размещать свои учетные данные) и один контекст для каждого потока, как (единственный) клиент узнает, какие учетные данные использовать для каждого потока??? Спасибо за ваше время заранее! - person Francisco Carriedo Scher; 09.10.2013
comment
@FranciscoCarriedoScher: я не уверен, что понимаю трудности, с которыми вы здесь столкнулись. Абсолютно НИЧЕГО не мешает вам иметь отдельного поставщика учетных данных для каждого потока/контекста. - person ok2c; 09.10.2013
comment
Трудность заключалась в том, чтобы удерживать один CredentialsProvider для всех потоков (поскольку он, кажется, внутренне подготовлен для управления набором учетных данных), но в итоге кажется, что, по крайней мере, BasicCredentialsProvider управляет только одними учетными данными для каждого экземпляра. Таким образом, использование нового экземпляра поставщика учетных данных для каждого потока кажется правильным, и ваш пример работает нормально. - person Francisco Carriedo Scher; 10.10.2013
comment
Что происходит, когда один поток собирается сделать несколько запросов. Некоторым нужна базовая авторизация, другим нет. Есть ли способ очистить контекст? Я действительно не понимаю, почему Apache не предоставляет способ передать учетные данные клиенту для определенного запроса. - person ryber; 17.12.2014
comment
@ryber: О да, эти идиоты. А что, если выполнение запроса включает в себя несколько перенаправлений в разные области аутентификации, а также прокси-аутентификацию вдобавок к этому или любую комбинацию нескольких десятков параметров или стратегий настройки? - person ok2c; 17.12.2014
comment
Я не знаю, зайду ли я так далеко, чтобы назвать их идиотами, но просто странно, что они не поддерживают метод, который доступен почти во всех других известных мне HTTP-клиентах. Ява или что-то другое. - person ryber; 18.12.2014
comment
В моем примере. У меня есть коллекция URL-адресов в базе данных. У кого-то есть полномочия, у кого-то нет. Я собираюсь перебрать их, вызвать каждый и собрать результаты. Каждый запуск может иметь различный набор URL-адресов. Я не думаю, что это странный сценарий, - person ryber; 18.12.2014
comment
@ryber: HTTP-аутентификация намного сложнее, чем может представить вульгарная комбинация имени пользователя и пароля. Если Apache HttpClient слишком сложен на ваш вкус, вы можете использовать любой другой HTTP-клиент, Java или любой другой. - person ok2c; 18.12.2014

У меня похожая проблема.

Я должен вызвать службу n раз с одним системным пользователем, прошедшим аутентификацию с помощью NTLM. Я хочу сделать это, используя несколько потоков. Я придумал создать один HTTPClient без поставщика учетных данных по умолчанию. Когда необходимо выполнить запрос, я использую внедренную CredentialProviderFactory в метод, выполняющий запрос (в определенном потоке). Используя это, я получаю новый CredentialsProvider и помещаю его в контекст (созданный в потоке). Затем я вызываю метод execute на клиенте, используя перегрузку execute(method, context).

class MilestoneBarClient implements IMilestoneBarClient {

private static final Logger log = LoggerFactory.getLogger(MilestoneBarClient.class);
private MilestoneBarBuilder builder;
private CloseableHttpClient httpclient;
private MilestoneBarUriBuilder uriBuilder;
private ICredentialsProviderFactory credsProviderFactory;


MilestoneBarClient(CloseableHttpClient client, ICredentialsProviderFactory credsProviderFactory, MilestoneBarUriBuilder uriBuilder) {
    this(client, credsProviderFactory, uriBuilder, new MilestoneBarBuilder());
}

MilestoneBarClient(CloseableHttpClient client, ICredentialsProviderFactory credsProviderFactory, MilestoneBarUriBuilder uriBuilder, MilestoneBarBuilder milestoneBarBuilder) {
    this.credsProviderFactory = credsProviderFactory;
    this.uriBuilder = uriBuilder;
    this.builder = milestoneBarBuilder;
    this.httpclient = client;
}

// This method is called by multiple threads
@Override
public MilestoneBar get(String npdNumber) {
    log.debug("Asking milestone bar info for {}", npdNumber);

    try {
        String url = uriBuilder.getPathFor(npdNumber);
        log.debug("Building request for URL {}", url);
        HttpClientContext localContext = HttpClientContext.create();
        localContext.setCredentialsProvider(credsProviderFactory.create());

        HttpGet httpGet = new HttpGet(url);

        long start = System.currentTimeMillis();
        try(CloseableHttpResponse resp = httpclient.execute(httpGet, localContext)){
[...]

По некоторым причинам я иногда получаю сообщение об ошибке, но я предполагаю, что это проблема NTLMCredentials (не потокобезопасность...).

В вашем случае вы, вероятно, могли бы передать фабрику методам get вместо передачи создания.

person Adhara    schedule 28.10.2015