I'm trying to do Digest mostly (or Basic) Authentication using RestTemplate and httpclient (4.x).
Since I couldn't find any relevant examples of how to actually do this, I have attempted various ways to hook the various httpclient artifacts, with no luck - essentially, no Authentication header is sent at all.
My current implementation is:
DefaultHttpClient newHttpClient = new DefaultHttpClient();
Credentials credentials = new UsernamePasswordCredentials( username, password );
AuthScope authScope = new AuthScope( host, port, AuthScope.ANY_REALM );
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials( authScope, credentials );
newHttpClient.setCredentialsProvider( credentialsProvider );
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory( newHttpClient );
restTemplate.setRequestFactory( requestFactory );
Is there something I'm doing wrong? Is there also a working example for this anywhere? Any help is appreciated. Thanks.
With the latest version of Spring and HttpClient they made it really easy to do both basic and digest authentication.
Note: I'm using Spring Boot 2.x.x (Spring Framework 5.x.x) and HttpClient 4.5.x
We can configure the RestTemplate to do either preemptive or non-preemptive (default) basic or digest authentication.
Non-Preemptive Basic or Digest Auth Setup
The setup for the RestTemplate to use non-preemptive (i.e. initially doing a challenge request) basic or digest authentication is the same. Just provide the username and password through the HttpClient library's CredentialsProvider class.
HttpClient will automatically detect which authentication type the server is using based on the 401 response header of the initial request (challenge request) thus no need to do any authentication type specific configuration.
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
Credentials credentials = new UsernamePasswordCredentials(username, password);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
HttpClient httpClient = HttpClients
.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
return builder
.requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
.build();
}
Preemptive Basic Auth Setup
With preemptive basic authentication its even easier, Spring supports it out of the box. Since only the username and password are needed it is advisable to use preemptive basic auth to remove the extra cost of doing the challenge request.
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.basicAuthorization(username, password)
.build();
}
Preemptive Digest Auth Setup
Spring doesn't support preemptive digest auth for RestTemplate out of the box. As digest auth requires the nonce and possibly other server generated data (e.g. opaque) aside from the username and password for it to authenticate itself so at least one challenge request must be made.
Given this it is still possible to use preemptive digest auth albeit using HttpClient's library directly. If you still like to use digest preemptive auth with RestTemplate note that some issues might be encountered when connecting to non Spring digest protected applications.
Refer to previous answers to use preemptive digest auth. Personally I don't suggest using preemptive digest auth with RestTemplate given the complexity and issues that can be encountered. The primary motivation of using preemptive digest auth is for performance purposes so unless you're doing numerous calls per request non-preemptive digest auth might be a better option.
You don't need any authentication type specific handling when using RestTemplate to send request. It doesn't matter if you're using preemptive or non-preemptive authentication, the code for sending requests will just be the same.
Sample GET Request
String response = restTemplate.getForObject(url, String.class);
JSONObject result = new JSONObject(response);
Sample POST Request
JSONObject body = new JSONObject();
// populate body
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(body, headers);
String response = restTemplate.postForObject(url, request, String.class);
JSONObject result = new JSONObject(response);
Try implementing your own RequestFactory in order to achieve preemptive authentication.
public class PreEmptiveAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
public PreEmptiveAuthHttpRequestFactory(DefaultHttpClient client) {
super(client);
}
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
AuthCache authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort());
authCache.put(targetHost, basicAuth);
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
return localcontext;
}
}
An then just use it:
HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory( newHttpClient );
Hope it helps
how to set the username and password (Copied from @bifur's comment)
You can use UserNamePasswordCredentials
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(getUsername(),getPassword());
client.getCredentialsProvider().setCredentials(new AuthScope(getHost(), getPort(), AuthScope.ANY_REALM), credentials);
And just use the client in the previous factory
HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory(client);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With