I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

This tutorial will illustrate how to configure Basic Authentication on the Apache HttpClient 4.

If you want to dig deeper and learn other cool things you can do with the HttpClient – head on over to the main HttpClient tutorial.

Further reading:

HttpAsyncClient Tutorial

HttpAsyncClient Tutorial - send a basic GET request, use the multi-threaded client, set up the client with SSL as well as with a proxy, and finally - do authentication.

Read more

Advanced HttpClient Configuration

HttpClient configurations for advanced use cases.

Read more

2. Basic Authentication with the API

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.ANY, credentials);
 
HttpClient client = HttpClientBuilder.create()
  .setDefaultCredentialsProvider(provider)
  .build();

HttpResponse response = client.execute(
  new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION));
int statusCode = response.getStatusLine()
  .getStatusCode();
 
assertThat(statusCode, equalTo(HttpStatus.SC_OK));

As you can see, creating the client with a credentials provider to set it up with Basic Authentication is not difficult.

Now, to understand what HttpClient will actually do behind the scenes, we’ll need to look at the logs:

# ... request is sent with no credentials
[main] DEBUG ... - Authentication required
[main] DEBUG ... - localhost:8080 requested authentication
[main] DEBUG ... - Authentication schemes in the order of preference: 
  [negotiate, Kerberos, NTLM, Digest, Basic]
[main] DEBUG ... - Challenge for negotiate authentication scheme not available
[main] DEBUG ... - Challenge for Kerberos authentication scheme not available
[main] DEBUG ... - Challenge for NTLM authentication scheme not available
[main] DEBUG ... - Challenge for Digest authentication scheme not available
[main] DEBUG ... - Selected authentication options: [BASIC]
# ... the request is sent again - with credentials

The entire Client-Server communication is now clear:

  • the Client sends the HTTP Request with no credentials
  • the Server sends back a challenge
  • the Client negotiates and identifies the right authentication scheme
  • the Client sends a second Request, this time with credentials

3. Preemptive Basic Authentication

Out of the box, the HttpClient doesn’t do preemptive authentication – this has to be an explicit decision made by the client.

First, we need to create the HttpContext – pre-populating it with an authentication cache with the right type of authentication scheme pre-selected. This will mean that the negotiation from the previous example is no longer necessary – Basic Authentication is already chosen:

HttpHost targetHost = new HttpHost("localhost", 8080, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, 
  new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS));

AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());

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

Now we can use the client with the new context and send the pre-authentication request:

HttpClient client = HttpClientBuilder.create().build();
response = client.execute(
  new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION), context);

int statusCode = response.getStatusLine().getStatusCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));

Let’s look at the logs:

[main] DEBUG ... - Re-using cached 'basic' auth scheme for http://localhost:8080
[main] DEBUG ... - Executing request GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1
[main] DEBUG ... >> GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1
[main] DEBUG ... >> Host: localhost:8080
[main] DEBUG ... >> Authorization: Basic dXNlcjE6dXNlcjFQYXNz
[main] DEBUG ... << HTTP/1.1 200 OK
[main] DEBUG ... - Authentication succeeded

Everything looks OK:

  • the “Basic Authentication” scheme is pre-selected
  • the Request is sent with the Authorization header
  • the Server responds with a 200 OK
  • Authentication succeeds

4. Basic Auth with Raw HTTP Headers

Preemptive Basic Authentication basically means pre-sending the Authorization header.

So – instead of going through the rather complex previous example to set it up, we can take control of this header and construct it by hand:

HttpGet request = new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION);
String auth = DEFAULT_USER + ":" + DEFAULT_PASS;
byte[] encodedAuth = Base64.encodeBase64(
  auth.getBytes(StandardCharsets.ISO_8859_1));
String authHeader = "Basic " + new String(encodedAuth);
request.setHeader(HttpHeaders.AUTHORIZATION, authHeader);

HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(request);

int statusCode = response.getStatusLine().getStatusCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));

Let’s make sure this is working correctly:

[main] DEBUG ... - Auth cache not set in the context
[main] DEBUG ... - Opening connection {}->http://localhost:8080
[main] DEBUG ... - Connecting to localhost/127.0.0.1:8080
[main] DEBUG ... - Executing request GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1
[main] DEBUG ... - Proxy auth state: UNCHALLENGED
[main] DEBUG ... - http-outgoing-0 >> GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1
[main] DEBUG ... - http-outgoing-0 >> Authorization: Basic dXNlcjE6dXNlcjFQYXNz
[main] DEBUG ... - http-outgoing-0 << HTTP/1.1 200 OK

So, even though there is not auth cache, Basic Authentication is still performed correctly and the 200 OK is sent back.

5. Conclusion

This article illustrated various ways to set up and use basic authentication with the Apache HttpClient 4.

As always, the code presented in this article is available over on Github. This is a Maven based project, so it should be easy to import and run as it is.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
TowiPanda
Guest
TowiPanda

Thank you

This resolved my problem with NTLM Authentication, now I just need to consume wsdl service with CXF.

Eugen Paraschiv
Guest

Hey Towi – glad you found it useful. Cheers,
Eugen.

mikec711
Guest
mikec711

this is very good, but what if I’m multiThreaded (ie: emulating n virtual users) and I want to have different uid/pw combos on different threads. I get my CloseableHttpClient from a PoolingHttpClientConnectionManager and, for each request, I get a HttpGet and use the CloseableHttpClient with: CloseableHttpResponse chr = httpClient.execute(httpGet) ; // So I’m wondering, do I get a separate httpClient per thread and set it that way and leave everything else the same? I tried the preEmptive route, but with that, I am suddenly getting an odd error: java.lang.IllegalArgumentException: Cookie name “Path” is a reserved token at javax.servlet.http.Cookie.(Cookie.java:139) (I don’t… Read more »

Eugen Paraschiv
Guest

You’ll need to set up a different HttpContext for each thread – that’s the recommended way of handling that in a multithreaded env. Hope that helps. Cheers,
Eugen.

mikec711
Guest
mikec711

Thanks very much. This makes sense. I am now seeing 401s (even after reTries). My actual execute is now and a debug statement after is: CloseableHttpResponse htResp = (useAuth) ? httpClient.execute(httpGet, httpClientContext) : httpClient.execute(httpGet) ; if (useAuth && hcdLogger.isLoggable(Level.FINER)) hcdLogger.logp(Level.FINER, thisClass+”OneUser”, METHOD, “statusCd: {0} httpCliCtx {1}”, new Object [] { htResp.getStatusLine().getStatusCode(), httpClientContext.getCredentialsProvider().getCredentials(AuthScope.ANY).getUserPrincipal()}); The log shows: FINER: statusCd: 401 httpCliCtx [principal: mobusr2] I originally set up the client context when I constructed the thread with: httpClientContext = HttpClientContext.create() ; httpClientContext.setCredentialsProvider(credentialsProvider[credProIdx]) ; (index is that I set up a certain number of credential providers (3 in this case) and use or reUse… Read more »

Eugen Paraschiv
Guest

Hey Mike – so, since these examples include a fair ammount of detail and code, the best way to deal with that is to get it on github. Copy-pasting code here in the comments is a bit sub-optimal. So – a small project with a test reproducing the issue would be great – and I’ll be happy to take a look. Cheers,
Eugen.

mikec711
Guest
mikec711

Thanks again for your help. I am not well versed in git, but using the web interface, I believe I have put all the code (1 java, plus the properties file, url list, and script I use to invoke it. You can ignore the variable substitution, but the code is there. Basic auth concept is I read a list of userids and passwords and create a CredentialsProvider for each. I then fire up n threads (specified in the startup) and distribute the CredentialsProviders among the threads (in my sample, I have 3 uids and 2 threads, so only the first… Read more »

Eugen Paraschiv
Guest

Hey Mike – sorry for the late response, I was on vacation and I’m going through all the comments now. So – on the github project – it’s great that you managed to get that to github, but I think the project will still benefit from a bit of cleanup, as it’s 500 lines of code, some not relevant for your problem and most far to complex for exemplifying what the issue is. My suggestion is this – get rid of everything that doesn’t strictly have to do with the problem – I think you should be able to cut… Read more »

mikec711
Guest
mikec711

Thanks Eugen, Ironically, I am on vacation now and away from that computer (under threat of death from the wife) … but I like your suggestion and will create a simpler version. It will still have to be multiThreaded, but I should still be able to keep it simpler. One other point is that only 7 of the 8 requests in each loop require basic authentication (the other is a home page). But I will work on this as soon as I get back to civilization. Thanks,

Eugen Paraschiv
Guest

I know that threat well 🙂 Sure thing, there’s no rush – let me know when you had the chance to do it. Cheers,
Eugen.

mikec711
Guest
mikec711

Hello Eugen, I have finally gotten a simplified version done. I have uploaded it all into github. Specifically, one java class SimpleHttpClientDriver.java that does it all, and the newLog0.log is the newest debug log from this run. It is a little simpler now. I just hardCode a list of URLs (some w/some mandatory substitution, but the subst is not really important. I just kick off 7 threads on 7 different uid/pw combinations (that are also hard-coded into the code). In the main, I set up the httpClient, then I create/start a thread for each userid in my array . In… Read more »

mikec711
Guest
mikec711

Just an aside, I also putting the logging.properties file I use which turns on a trace in this simpler code (just over 200 lines of code and much simpler) … plus it also turns on some tracing w/in the httpClient code itself. I am hoping that this more streamLined code will have you look at it and say … “can this guy read?? He did x instead of y and that’s just moronic.” I’m happy to be called a moron if someone can point me to the error in my ways. Thanks,

mikec711
Guest
mikec711

Small update, I reUpdated what’s out on git. Have tried many items. Latest changes involve authCache, use of an authScope that is specific (my host and my IP). When I do a setHeader and create the auth, I get a weird error: Exception thrown by application class ‘javax.servlet.http.Cookie.():139’   Cookie name "Path" is a reserved token   at javax.servlet.http.Cookie.(Cookie.java:139) I don’t force any cookies. I get a 200 on the one request (in each loop) that is NOT secured (ie: the Authorization header ignored) and a 500 on the requests where they look at it. When I use the httpContext and credentialProviders,… Read more »

mikec711
Guest
mikec711

One more item, I did get a client trace which seems to verify that the header was indeed not there. Wondering if it has do with realm?

mikec711
Guest
mikec711

One other small item, many classes are in multiple packages which may mean I’m pulling some wrong class version. I did see an entry that seemed to have similar symptoms to mine suggest:

new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT)

I saw I could not use the client AuthScope for that as the setCredentials would not take that,
but I did try it and got the same 401 behavior. Thanks again again.

Nirmal
Guest
Nirmal

Hi Eugene, even with the second approach I can see server first responded with “HTTP/1.1 401 Unauthorized” and then server challenges again. why it is so. what I am missing?

Eugen Paraschiv
Guest

Well, it depends – you’ll have to enable the extra logging that the HttpClient gives you and carefully study it to understand the entire communication. Depending on that and based on what the server responds with – you should be able to figure out the problem. Otherwise, with the limited info here – I’d say that you’re simply not providing the right authentication information that the server challenged you with.
Hope it helps. Cheers,
Eugen.

Lajos Incze
Guest
Lajos Incze

i would use iso-8859-1 (as the “most common”, quasi standard encoding scheme accepted by most browser, see http://stackoverflow.com/questions/702629/utf-8-characters-mangled-in-http-basic-auth-username/703341#703341 ), instead of ascii at the direct header manipulation.

Eugen Paraschiv
Guest

That makes sense, as ISO-8859-1 is can do more. I was of the impression that it’s mostly European, but since Firefox uses it by default – it’s probably not. Cheers,
Eugen.

ImTiaZ
Guest
ImTiaZ

Hi Eugen,
I successfully done HTTP BASIC auth with GET following this article.
But failed to do HTTP BASIC authentication with POST method.
Can you please help me with this. How can I get in touch with you.
Thanks

Eugen Paraschiv
Guest

Generally speaking, POST should work the same way.
And to answer your question about getting in touch – email is the best option. If you’re on the list, just reply to any of the emails you get – these go right into my inbox. Cheers,
Eugen.