Преимущественная базовая аутентификация с Apache HttpClient 4 - PullRequest
47 голосов
/ 06 января 2010

Есть ли более простой способ настроить клиент http для упреждающей базовой аутентификации, чем тот, который описан здесь ?
В предыдущей версии (3.x) это был простой вызов метода (например, httpClient.getParams().setAuthenticationPreemptive(true)).
Главное, чего я хочу избежать - это добавление BasicHttpContext к каждому выполняемому методу.

Ответы [ 9 ]

87 голосов
/ 01 декабря 2010

Если вы хотите заставить HttpClient 4 аутентифицироваться с помощью одного запроса, будет работать следующее:

String username = ...
String password = ...
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);

HttpRequest request = ...
request.addHeader(new BasicScheme().authenticate(creds, request));
22 голосов
/ 16 августа 2010

Трудно сделать это, не пропуская контекст каждый раз, но вы, вероятно, можете сделать это, используя перехватчик запросов.Вот некоторый код, который мы используем (найден по их JIRA, iirc):

// Pre-emptive authentication to speed things up
BasicHttpContext localContext = new BasicHttpContext();

BasicScheme basicAuth = new BasicScheme();
localContext.setAttribute("preemptive-auth", basicAuth);

httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);

(...)

static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {

    public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
        AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);

        // If no auth scheme avaialble yet, try to initialize it
        // preemptively
        if (authState.getAuthScheme() == null) {
            AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
            CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
            HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
            if (authScheme != null) {
                Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
                if (creds == null) {
                    throw new HttpException("No credentials for preemptive authentication");
                }
                authState.setAuthScheme(authScheme);
                authState.setCredentials(creds);
            }
        }

    }

}
17 голосов
/ 08 августа 2012

Это то же решение, что и у Mat's Mannion, но вам не нужно ставить localContext для каждого запроса. Это проще, но добавляет аутентификацию ко ВСЕМ запросам. Полезно, если у вас нет контроля над отдельными запросами, как в моем случае при использовании Apache Solr, который использует HttpClient для внутреннего использования.

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);

(...)

static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {

    public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
        AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);

        // If no auth scheme available yet, try to initialize it
        // preemptively
        if (authState.getAuthScheme() == null) {
            CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
            HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
            Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
            if (creds == null) {
                throw new HttpException("No credentials for preemptive authentication");
            }
            authState.setAuthScheme(new BasicScheme());
            authState.setCredentials(creds);
        }

    }

}

Конечно, вы должны установить провайдера учетных данных:

httpClient.getCredentialsProvider().setCredentials(
                new AuthScope(url.getHost(), url.getPort()),
                new UsernamePasswordCredentials(username, password))

AuthScope не должен содержать царство, так как оно не известно заранее.

7 голосов
/ 12 марта 2015

Многие ответы выше используют устаревший код. Я использую Apache SOLRJ версии 5.0.0. Мой код состоит из

private HttpSolrClient solrClient; 

private void initialiseSOLRClient() {
            URL solrURL = null;
            try {
                solrURL = new URL(urlString);
            } catch (MalformedURLException e) {
                LOG.error("Cannot parse the SOLR URL!!" + urlString);
                throw new SystemException("Cannot parse the SOLR URL!! " + urlString, e);
            }
            String host = solrURL.getHost();
            int port = solrURL.getPort();
            AuthScope authScope = new AuthScope(host, port);

    BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
    textEncryptor.setPassword("red bananas in the spring");
    String decryptPass = textEncryptor.decrypt(pass);
    UsernamePasswordCredentials creds = new UsernamePasswordCredentials(userName, decryptPass);

    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(
            authScope,
            creds);

    HttpClientBuilder builder = HttpClientBuilder.create();
    builder.addInterceptorFirst(new PreemptiveAuthInterceptor());
    builder.setDefaultCredentialsProvider(credsProvider);
    CloseableHttpClient httpClient = builder.build();

    solrClient = new HttpSolrClient(urlString, httpClient);
}

Теперь PreemptiveAuthInterceptor выглядит следующим образом: -

static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {

    public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
        AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
        // If no auth scheme available yet, try to initialize it
        // preemptively
        if (authState.getAuthScheme() == null) {
            CredentialsProvider credsProvider = (CredentialsProvider) 
                        context.getAttribute(HttpClientContext.CREDS_PROVIDER);
            HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
            AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
            Credentials creds = credsProvider.getCredentials(authScope);
            if(creds == null){

            }
            authState.update(new BasicScheme(), creds);
        }

    }
}
6 голосов
/ 20 января 2014

Я думаю, что лучше всего сделать это вручную. Я добавил следующую функцию

Классическая Java:

import javax.xml.bind.DatatypeConverter;

...

private static void addAuthHeader(HttpRequestBase http, String username, String password) throws UnsupportedEncodingException {
        String encoded = DatatypeConverter.printBase64Binary((username + ":" + password).getBytes("UTF-8"));
        http.addHeader("AUTHORIZATION", "Basic " + encoded);
    }

HTTPRequestBase может быть экземпляром HttpGet или HttpPost

Android:

import android.util.Base64;

...

private static void addAuthHeader(HttpRequestBase http, String username, String password) throws UnsupportedEncodingException {
    String encoded = Base64.encodeToString((username + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP);
    http.addHeader("AUTHORIZATION", "Basic " + encoded);
}
6 голосов
/ 07 июля 2011

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

HttpPost httppost = new HttpPost(url);
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
Header bs = new BasicScheme().authenticate(creds, httppost);
httppost.addHeader("Proxy-Authorization", bs.getValue());

Мысль, которая может быть полезна всем, кто сталкивается с этим.

1 голос
/ 16 марта 2017

Я использую этот код, основываясь на моем чтении HTTPClient 4.5 документов :

HttpClientContext ctx = HttpClientContext.create()
ctx.setCredentialsProvider(new BasicCredentialsProvider())
ctx.setAuthCache(new BasicAuthCache())
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user, pass)
AuthScope authScope = new AuthScope(host, port)
ctx.getCredentialsProvider.setCredentials(authScope, credentials)

// This part makes authentication preemptive:
HttpHost targetHost = new HttpHost(host, port, scheme)
ctx.getAuthCache.put(targetHost, new BasicScheme())

... и убедитесь, что вы всегда передаете этот контекст HTTPClient.execute().

0 голосов
/ 29 января 2014

в андроиде, ответ Мат Мэнниона не может разрешить https, все равно отправьте два запроса, вы можете сделать, как показано ниже, трюк в добавлении authHeader с user-agent:

    public static DefaultHttpClient createProxyHttpClient() {
        try {
            final DefaultHttpClient client = createPlaintHttpClient();
            client.setRoutePlanner(new HttpRoutePlanner() {
                @Override
                public HttpRoute determineRoute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
                    boolean isSecure = "https".equalsIgnoreCase(target.getSchemeName());
                    if (needProxy) {
                        Header header = isSecure ? ProxyUtils.createHttpsAuthHeader() : ProxyUtils.createAuthHeader();
                        if (isSecure) {
                            client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT + "\r\n" + header.getName() + ":" + header.getValue());
                        } else {
                            client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT);
                            if (request instanceof RequestWrapper) {
                                request = ((RequestWrapper) request).getOriginal();
                            }
                            request.setHeader(header);
                        }
                        String host = isSecure ? ProxyUtils.SECURE_HOST : ProxyUtils.HOST;
                        int port = isSecure ? ProxyUtils.SECURE_PORT : ProxyUtils.PORT;
                        return new HttpRoute(target, null,  new HttpHost(host, port), isSecure);
                    } else {
                        client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT);
                        return new HttpRoute(target, null, isSecure);
                    }
                }
            });
            return client;
        } catch (Exception e) {
            e.printStackTrace();
            return new DefaultHttpClient();
        }
    }

public static DefaultHttpClient createPlaintHttpClient() {
       try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            PlainSSLSocketFactory socketFactory = new PlainSSLSocketFactory(trustStore);
            socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            BasicHttpParams params = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(params, 30000);
            HttpConnectionParams.setSoTimeout(params, 30000);
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            registry.register(new Scheme("https", socketFactory, 443));
            ThreadSafeClientConnManager ccm = new ThreadSafeClientConnManager(params, registry);
            HttpClientParams.setCookiePolicy(params, CookiePolicy.BROWSER_COMPATIBILITY);
            final DefaultHttpClient client = new DefaultHttpClient(ccm, params);
            client.setRoutePlanner(new HttpRoutePlanner() {
        @Override
        public HttpRoute determineRoute(HttpHost target, HttpRequest arg1, HttpContext arg2) throws HttpException {
               client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, com.netease.cloudmusic.utils.HttpRequest.USER_AGENT);
            return new HttpRoute(target, null, "https".equalsIgnoreCase(target.getSchemeName()));
        }
        });
            return client;
        } catch (Exception e) {
            e.printStackTrace();
            return new DefaultHttpClient();
        }
}
0 голосов
/ 06 января 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...