Преимущественная базовая аутентификация с помощью Apache HttpClient 4

StackOverflow https://stackoverflow.com/questions/2014700

Вопрос

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

Это было полезно?

Решение

Трудно сделать это, не передавая контекст каждый раз, но вы, вероятно, можете сделать это, используя перехватчик запросов.Вот некоторый код, который мы используем (найден в их 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);
            }
        }

    }

}

Другие советы

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

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

HttpRequest request = ...
request.addHeader(new BasicScheme().authenticate(creds, request));

Это то же самое решение, что и у 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 не должно содержать области, так как она не известна заранее.

Во многих приведенных выше ответах используется устаревший код.Я использую 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);
        }

    }
}

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

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

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

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

Классическая 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);
}

Я использую этот код, основанный на моем прочтении документы 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().

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

Я бы подумал, что лучше всего иметь свой собственный объект, который настраивает весь мусор, необходимый для преимущественной авторизации, и имеет простой метод или методы для выполнения запросов к заданным объектам HttpMethod.

в Android ответ Мэта Манниона не может разрешить 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();
        }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top