Preemptive Basic Authentication with Apache Httpclient 4

Preemptive Basic authentication with Apache HttpClient 4

It's difficult to do this without passing a context through every time, but you can probably do it by using a request interceptor. Here is some code that we use (found from their 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);
}
}

}

}

How to do Preemptive authentication using Java 11 HTTP client?

The HttpClient behaves in the same way than HttpURLConnection in what preemptive authentication is concerned: for basic authentication it will preemptively send the credentials if it finds them in its cache. However, the cache is populated after the first successful request (or more exactly after the response headers indicating that the authentication was successful are parsed).

If this is not satisfactory for you then a possibility is to handle authentication directly in your code by preemptively inserting the Authorization header in your request, and not setting any Authenticator.

Apache HttpClient (4.1 and newer): how to do basic authentication?

We do basic authentication with HttpClient, but we do not use CredentialProvider. Here's the code:

HttpClient client = factory.getHttpClient(); //or any method to get a client instance
Credentials credentials = new UsernamePasswordCredentials(username, password);
client.getState().setCredentials(AuthScope.ANY, credentials);

UPDATE:
A stated in the comments, the HttpClient.getState() methos is available in version 3.x of the API. However, newer versions of the API doesn't support that method.

HttpClientBuilder basic auth

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.

Preemptive authentication with DefaultHttpClient

My solution is this here:

public class PreemptiveNTLMHeader implements Header {
private HttpRequest request;
private NTCredentials ntc;

public PreemptiveNTLMHeader(HttpRequest request, NTCredentials ntc) {
this.request = request;
this.ntc = ntc;
}

/* (non-Javadoc)
* @see org.apache.http.Header#getName()
*/
public String getName() {
return "Authorization";
}

/* (non-Javadoc)
* @see org.apache.http.Header#getValue()
*/
public String getValue() {
request.removeHeader(this);
try {
return "NTLM " + new JCIFSEngine().generateType1Msg(ntc.getDomain(), ntc.getWorkstation());
} catch(NTLMEngineException e) {
return "Failed";
}
}

/* (non-Javadoc)
* @see org.apache.http.Header#getElements()
*/
public HeaderElement[] getElements() throws ParseException {
return null;
}
}

With this usage:

NTCredentials ntc = new NTCredentials("example.com/user:password");
httpPost.addHeader(new PreemptiveNTLMHeader(httpPost, ntc));

So will the DefaultHttpClient send a Authorization: NTLM AAA... header which skips the initial request. After the first usage of this header this header deletes itself to avoid that this dummy header overrides the real auth process. This works for me.



Related Topics



Leave a reply



Submit