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

3

10

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.

Candlelight answered 21/2, 2012 at 11:13 Comment(2)
Would you post more detailed logs of the HTTP requests and responses? It would be useful to confirm things like the authentication challenge before trying to troubleshoot your client code in particular.Nickelic
Hopefully there should be no challenge - I'd like to have preemptive authentication. As for the client code - I'm really looking for any setup with Spring RestTemplate and HTTPClient (4.x) that works.Candlelight
C
9

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);
Calamondin answered 30/3, 2012 at 10:34 Comment(3)
One quick followup - how exactly are the username and password set?Candlelight
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);Calamondin
If you add the scheme this also supports https: HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());Aiaia
A
5

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);
Acantho answered 28/10, 2018 at 9:27 Comment(1)
I had inherited code attempting to do preemptive digest auth. This answer helped to identify this, replace it with non-preemptive which is simpler, throw away old code and eventually pass the authentication against Asterisk. Thanks.Holdup
A
0

I modified original answer a bit:

  • added https support
  • updated depracated stuff

    public class PreEmptiveAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
    
        public PreEmptiveAuthHttpRequestFactory(HttpClient 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(), uri.getScheme());
            authCache.put(targetHost, basicAuth);
            HttpClientContext localContext = HttpClientContext.create();
            localContext.setAuthCache(authCache);
            return localContext;
        }
    }
    
Affranchise answered 21/9, 2016 at 10:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.