使用Apache HttpClient进行抢占式基本身份验证4

时间:2010-01-06 17:05:46

标签: java httpclient basic-authentication http-basic-authentication

是否有一种更简单的方法来设置http客户端以进行抢占式基本身份验证,而不是描述here? 在以前的版本(3.x)中,它曾经是一个简单的方法调用(例如,httpClient.getParams().setAuthenticationPreemptive(true)) 我想避免的主要是将BasicHttpContext添加到我执行的每个方法中。

10 个答案:

答案 0 :(得分:87)

如果您希望强制HttpClient 4通过单个请求进行身份验证,则以下内容将起作用:

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

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

答案 1 :(得分:22)

如果没有每次传递上下文都很难做到这一点,但你可以通过使用请求拦截器来实现。这是我们使用的一些代码(从他们的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);
            }
        }

    }

}

答案 2 :(得分:17)

这与Mat的Mannion相同,但您不必将localContext放在每个请求中。它更简单,但它为所有请求添加了身份验证。如果您无法控制单个请求,则非常有用,就像我使用内部使用HttpClient的Apache Solr一样。

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不得包含领域,因为事先不知道。

答案 3 :(得分:7)

上面的很多答案都使用了弃用的代码。我使用的是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);
        }

    }
}

答案 4 :(得分:6)

派对有点晚了,但我试图通过线程来解决这个问题,以便对帖子请求进行代理预授权。为了增加Adam的回复,我发现以下内容对我有用:

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

对于遇到此问题的其他人来说,这可能会有所帮助。

答案 5 :(得分:6)

我认为最好的方法可能就是手动完成。我添加了以下功能

经典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可以是HttpGetHttpPost

的实例

的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 :(得分:1)

我正在使用此代码,基于我对the HTTPClient 4.5 docs的解读:

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()

答案 7 :(得分:0)

我没有得到你的结束评论。 HttpClient具有用于执行抢占式身份验证的所有机制,您只需执行一次(构建和配置HttpClient时)。完成后,您可以像往常一样构建方法实例。您没有“将BasicHttpContext”添加到方法中。

我认为,最好的办法是拥有自己的对象来设置抢占式身份验证所需的所有垃圾,并且有一个简单的方法来执行给定HTTPMethod对象的请求。

答案 8 :(得分:0)

在Android中,Mat Mannion的答案无法解析https,仍然发送两个请求,你可以像下面这样做,诀窍是用user-agent追加authHeader:

    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();
        }
}

答案 9 :(得分:0)

SolrConfig:

@Configuration
public class SolrConfig {

    @Value("${solr.http.url}")
    private String solrUrl;

    @Value("${solr.http.username}")
    private String solrUser;

    @Value("${solr.http.password}")
    private String solrPassword;

    @Value("${solr.http.pool.maxTotal}")
    private int poolMaxTotal;

    @Value("${solr.http.pool.maxPerRoute}")
    private int pollMaxPerRoute;

    @Bean
    public SolrClient solrClient() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(poolMaxTotal);
        connectionManager.setDefaultMaxPerRoute(pollMaxPerRoute);

        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(solrUser, solrPassword));

        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .addInterceptorFirst(new PreemptiveAuthInterceptor())
                .setConnectionManager(connectionManager)
                .setDefaultCredentialsProvider(credentialsProvider)
                .build();

        return new HttpSolrClient.Builder(solrUrl).withHttpClient(httpClient).build();
    }


}

PreemptiveAuthInterceptor:

public class PreemptiveAuthInterceptor implements HttpRequestInterceptor {

    public void process(final HttpRequest request, final HttpContext context)
            throws HttpException {
        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 credentialsProvider = (CredentialsProvider) context
                    .getAttribute(HttpClientContext.CREDS_PROVIDER);
            HttpHost targetHost = (HttpHost) context
                    .getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
            Credentials credentials = credentialsProvider.getCredentials(new AuthScope(
                    targetHost.getHostName(), targetHost.getPort()));
            if (credentials == null) {
                throw new HttpException(
                        "No credentials for preemptive authentication");
            }
            authState.update(new BasicScheme(), credentials);
        }

    }

}
相关问题