Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RestTemplate basic or digest Authentication with the current httpclient (4.x)

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.

like image 371
Eugen Avatar asked Feb 21 '12 11:02

Eugen


2 Answers

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


Configure RestTemplate

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.


Send request using RestTemplate

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);
like image 26
Max Ramos Avatar answered Oct 24 '22 21:10

Max Ramos


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);
like image 87
bifur Avatar answered Oct 24 '22 19:10

bifur