Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClientBuilder basic auth

Tags:

Since HttpClient 4.3, I have been using the HttpClientBuilder. I am connecting to a REST service that has basic authentication. I am setting the credentials as follows:

HttpClientBuilder builder = HttpClientBuilder.create();

// Get the client credentials
String username = Config.get(Constants.CONFIG_USERNAME);
String password = Config.get(Constants.CONFIG_PASSWORD);

// If username and password was found, inject the credentials
if (username != null && password != null)
{
    CredentialsProvider provider = new BasicCredentialsProvider();

    // Create the authentication scope
    AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM);

    // Create credential pair
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);

    // Inject the credentials
    provider.setCredentials(scope, credentials);

    // Set the default credentials provider
    builder.setDefaultCredentialsProvider(provider);
}

However, this does not work (the REST service that I am using is returning 401). What is going wrong?

like image 525
Sayak Banerjee Avatar asked Jan 03 '14 23:01

Sayak Banerjee


People also ask

How do I add basic authentication to HttpClient Java?

Let's add an authenticator to our client: HttpClient client = HttpClient. newBuilder() . authenticator(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("postman", "password".

How do I pass HttpClient credentials?

Let's start with the standard way of configuring Basic Authentication on the HttpClient – via a CredentialsProvider: CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user1", "user1Pass"); provider. setCredentials(AuthScope.

What is preemptive basic authentication?

Preemptive basic authentication is the practice of sending http basic authentication credentials (username and password) before a server replies with a 401 response asking for them. This can save a request round trip when consuming REST apis which are known to require basic authentication.


1 Answers

From the Preemptive Authentication Documentation here:

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

By default, httpclient will not provide credentials preemptively, it will first create a HTTP request without authentication parameters. This is by design, as a security precaution, and as part of the spec. But, this causes issues if you don't retry the connection, or wherever you're connecting to expects you to send authentication details on the first connection. It also causes extra latency to a request, as you need to make multiple calls, and causes 401s to appear in the logs.

The workaround is to use an authentication cache to pretend that you've already connected to the server once. This means you'll only make one HTTP call and won't see a 401 in the logs:

CloseableHttpClient httpclient = HttpClientBuilder.create().build();

HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
        new AuthScope(targetHost.getHostName(), targetHost.getPort()),
        new UsernamePasswordCredentials("username", "password"));

// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);

// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);

HttpGet httpget = new HttpGet("/");
for (int i = 0; i < 3; i++) {
    CloseableHttpResponse response = httpclient.execute(
            targetHost, httpget, context);
    try {
        HttpEntity entity = response.getEntity();

    } finally {
        response.close();
    }
}

Please note: You need to trust the host you're connecting to, and if you're using HTTP, your username and password will be sent in cleartext (well, base64, but that doesn't count).

You should also be using a much more specific Authscope rather than relying on AuthScope .ANY_HOST and AuthScope.ANY_PORT like in your example.

like image 141
Cetra Avatar answered Oct 17 '22 05:10

Cetra