6

Edit: I have already tried the solutions provided in the comments and they don't work. Adding raw headers works, but I am trying to avoid doing that.

I am facing a bizarre situation. I have a resource protected by basic auth which I am able to query easily via browser, Postman and curl. But in Java, I am not able to query it and getting a 403 forbidden error. I followed the below example:

CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("username", "password");
provider.setCredentials(AuthScope.ANY, credentials);
CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();

HttpResponse response = client.execute(new HttpGet("https://hostname.com/api/detail?query=xx"));
int statusCode = response.getStatusLine().getStatusCode();
System.out.println(statusCode);     // prints 403 forbidden

When I inspected the objects in debugger, I noticed HttpGet object's headers were empty. That is, it wasn't passing the "Authorization" header with a "Basic asdflkhjWskjhakljhasdfkasdflkjh=" value.

So I manually added the header with the correct value and it worked (status 200 OK).

Can someone please help me figure out why this is happening and how can I use the HttpClient API to set the right headers instead of adding the header manually?

This is my dependency:

<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
Sun
  • 559
  • 2
  • 9
  • 30
  • Looks like a there's good example [here](https://www.baeldung.com/httpclient-4-basic-authentication) – Jamie_D Dec 08 '18 at 21:27
  • Possible duplicate of [Apache HttpClient (4.1 and newer): how to do basic authentication?](https://stackoverflow.com/questions/9402653/apache-httpclient-4-1-and-newer-how-to-do-basic-authentication) – Jon Sampson Dec 08 '18 at 21:28
  • That is exactly where I copied the above snippet from, @Jamie_D. – Sun Dec 08 '18 at 21:30
  • Try it with the **Raw Headers** method. – Jamie_D Dec 08 '18 at 21:33
  • Adding a raw header works. My question is how to avoid doing that. – Sun Dec 08 '18 at 21:36
  • 1
    Produce a wire / context log of the session as described here http://hc.apache.org/httpcomponents-client-4.5.x/logging.html and add it the question – ok2c Dec 10 '18 at 09:51

1 Answers1

1

Here's a debug log generated from running your code with the following arguments to the VM:

-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
-Dorg.apache.commons.logging.simplelog.showdatetime=true
-Dorg.apache.commons.logging.simplelog.log.org.apache.http=DEBUG
-Dorg.apache.commons.logging.simplelog.log.org.apache.http.wire=ERROR

The log:

RequestAddCookies - CookieSpec selected: default
RequestAuthCache - Auth cache not set in the context
PoolingHttpClientConnectionManager - Connection request: [route: {}->http://localhost:80][total kept alive: 0; route allocated: 0 of 2; total allocated: 0 of 20]
PoolingHttpClientConnectionManager - Connection leased: [id: 0][route: {}->http://localhost:80][total kept alive: 0; route allocated: 1 of 2; total allocated: 1 of 20]
MainClientExec - Opening connection {}->http://localhost:80
DefaultHttpClientConnectionOperator - Connecting to localhost/127.0.0.1:80
DefaultHttpClientConnectionOperator - Connection established 127.0.0.1:37638<->127.0.0.1:80
MainClientExec - Executing request GET /test/ HTTP/1.1
MainClientExec - Target auth state: UNCHALLENGED
MainClientExec - Proxy auth state: UNCHALLENGED
headers - http-outgoing-0 >> GET /test/ HTTP/1.1
headers - http-outgoing-0 >> Host: localhost
headers - http-outgoing-0 >> Connection: Keep-Alive
headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_131)
headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
headers - http-outgoing-0 << HTTP/1.1 401 Unauthorized
headers - http-outgoing-0 << Date: Mon, 10 Dec 2018 15:27:14 GMT
headers - http-outgoing-0 << Server: Apache/2.4.25 (Debian)
headers - http-outgoing-0 << WWW-Authenticate: Basic realm="Restricted Content"
headers - http-outgoing-0 << Content-Length: 456
headers - http-outgoing-0 << Keep-Alive: timeout=5, max=100
headers - http-outgoing-0 << Connection: Keep-Alive
headers - http-outgoing-0 << Content-Type: text/html; charset=iso-8859-1
MainClientExec - Connection can be kept alive for 5000 MILLISECONDS
HttpAuthenticator - Authentication required
HttpAuthenticator - localhost:80 requested authentication
TargetAuthenticationStrategy - Authentication schemes in the order of preference: [Negotiate, Kerberos, NTLM, Digest, Basic]
TargetAuthenticationStrategy - Challenge for Negotiate authentication scheme not available
TargetAuthenticationStrategy - Challenge for Kerberos authentication scheme not available
TargetAuthenticationStrategy - Challenge for NTLM authentication scheme not available
TargetAuthenticationStrategy - Challenge for Digest authentication scheme not available
HttpAuthenticator - Selected authentication options: [BASIC [complete=true]]
MainClientExec - Executing request GET /test/ HTTP/1.1
MainClientExec - Target auth state: CHALLENGED
HttpAuthenticator - Generating response to an authentication challenge using basic scheme
MainClientExec - Proxy auth state: UNCHALLENGED
headers - http-outgoing-0 >> GET /test/ HTTP/1.1
headers - http-outgoing-0 >> Host: localhost
headers - http-outgoing-0 >> Connection: Keep-Alive
headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_131)
headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
headers - http-outgoing-0 >> Authorization: Basic *snip*
headers - http-outgoing-0 << HTTP/1.1 200 OK
headers - http-outgoing-0 << Date: Mon, 10 Dec 2018 15:27:14 GMT
headers - http-outgoing-0 << Server: Apache/2.4.25 (Debian)
headers - http-outgoing-0 << Vary: Accept-Encoding
headers - http-outgoing-0 << Content-Encoding: gzip
headers - http-outgoing-0 << Content-Length: 397
headers - http-outgoing-0 << Keep-Alive: timeout=5, max=99
headers - http-outgoing-0 << Connection: Keep-Alive
headers - http-outgoing-0 << Content-Type: text/html;charset=UTF-8
MainClientExec - Connection can be kept alive for 5000 MILLISECONDS
HttpAuthenticator - Authentication succeeded
TargetAuthenticationStrategy - Caching 'basic' auth scheme for http://localhost:80

I want to point out the fact that the request is made twice by the client; once with a 401 Unauthorized failure which is missing the Authorization: Basic header and the second with the required header when the client realizes it needs authorization. If you're judging your debugger based on the initial request then you may have judged too quickly.

Jon Sampson
  • 1,473
  • 1
  • 21
  • 31
  • 2
    This is helpful. After debugging the API further, I noticed the very first time auth fails (by sending wrong credentials deliberately), it returns 403 Forbidden. I believe it should return 401 Unauthorized. So, perhaps (I am speculating here) HttpClient is expecting the 401 Unauthorized before it sends the credentials. If that's the case, the API isn't coded properly. I'll play around with the API and see if I can get it to return the right response upon auth failure. – Sun Dec 10 '18 at 19:11