Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring 4.0.0 basic authentication with RestTemplate

I am currently working on integration of a third party application with our local reporting system. I would like to implement REST calls with basic authentication but facing issues in Spring 4.0.0. I have a simple solution what works nicely:

final RestTemplate restTemplate = new RestTemplate();
final String plainCreds = "username:password";
final byte[] plainCredsBytes = plainCreds.getBytes();
final byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
final String base64Creds = new String(base64CredsBytes);

final HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
final HttpEntity<String> request = new HttpEntity<String>(headers);

final ResponseEntity<MyDto> response = restTemplate.exchange("myUrl", HttpMethod.GET, request, MyDto.class);
final MyDto dot = response.getBody();

but wanted to rewrite this to use ClientHttpRequestFactory in the following way:

final RestTemplate restTemplate = new RestTemplate(createSecureTransport("username", "password"));

private ClientHttpRequestFactory createSecureTransport(final String username, final String password) {
    final HttpClient client = new HttpClient();
    final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
    client.getState().setCredentials(new AuthScope(null, 9090, AuthScope.ANY_REALM), credentials);
    return new CommonsClientHttpRequestFactory(client);
}

This code is not compiling as the CommonsClientHttpRequestFactory class not exists anymore in Spring 4.0.0. Do somebody know any alternative solution to this? I am quite new in this REST world therefore any help will be appreciated.

like image 248
shippi Avatar asked Feb 24 '14 20:02

shippi


People also ask

How do you add basic auth in Spring boot RestTemplate?

Now that everything is in place, the RestTemplate will be able to support the Basic Authentication scheme just by adding a BasicAuthorizationInterceptor: restTemplate. getInterceptors(). add( new BasicAuthorizationInterceptor("username", "password"));

How do you pass basic auth in RestTemplate?

To enable basic authentication in RestTemplate for outgoing rest requests, we shall configure CredentialsProvider into HttpClient API. This HttpClient will be used by RestTemplate to send HTTP requests to backend rest apis. In this example, we are creating a Junit test which invokes a basic auth secured rest api.


5 Answers

I know that this is an old question, but I was looking for the answer to this myself. You need to add a RestTemplate interceptor when configuring the RestTemplate. An example below in annotation configuration:

@Bean
public RestTemplate restTemplate() {

    final RestTemplate restTemplate = new RestTemplate();

    restTemplate.setMessageConverters(Arrays.asList(
            new FormHttpMessageConverter(),
            new StringHttpMessageConverter()
    ));
    restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("client", "secret"));

    return restTemplate;
}

Javadoc for BasicAuthorizationInterceptor.

I was stuck on this for a good few hours. Maybe it will help somebody out in the near future.

like image 198
Shiraaz.M Avatar answered Oct 16 '22 23:10

Shiraaz.M


From http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1/ with HttpClient 4.3 edits:

Both Spring 3.0 and 3.1 and now 4.x have very good support for the Apache HTTP libraries:

  1. Spring 3.0, the CommonsClientHttpRequestFactory integrated with the now end of lifed HttpClient 3.x
  2. Spring 3.1 introduced support for the current HttpClient 4.x via HttpComponentsClientHttpRequestFactory (support added in the JIRA SPR-6180)
  3. Spring 4.0 introduced async support via the HttpComponentsAsyncClientHttpRequestFactory

Let’s start setting things up with HttpClient 4 and Spring 4.

The RestTemplate will require an HTTP request factory – a factory that supports Basic Authentication – so far, so good. However, using the existing HttpComponentsClientHttpRequestFactory directly will prove to be difficult, as the architecture of RestTemplate was designed without good support for HttpContext – an instrumental piece of the puzzle. And so we’ll need to subclass HttpComponentsClientHttpRequestFactory and override the createHttpContext method: (taken from soluvas-framework on GitHub)

package org.soluvas.commons.util;

import java.net.URI;

import javax.annotation.Nullable;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.HttpClient;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * From http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1/
 * 
 * <p>And with that, everything is in place – the {@link RestTemplate} will now be able to support the Basic Authentication scheme; a simple usage pattern would be:
 * 
 * <pre>
 * final AuthHttpComponentsClientHttpRequestFactory requestFactory = new AuthHttpComponentsClientHttpRequestFactory(
 *                  httpClient, host, userName, password);
 * final RestTemplate restTemplate = new RestTemplate(requestFactory);
 * </pre>
 *   
 * And the request:
 *
 * <pre>
 * restTemplate.get("http://localhost:8080/spring-security-rest-template/api/foos/1", Foo.class);
 * </pre>
 * 
 * @author anton
 */
public class AuthHttpComponentsClientHttpRequestFactory extends
        HttpComponentsClientHttpRequestFactory {

    protected HttpHost host;
    @Nullable
    protected String userName;
    @Nullable
    protected String password;

    public AuthHttpComponentsClientHttpRequestFactory(HttpHost host) {
        this(host, null, null);
    }

    public AuthHttpComponentsClientHttpRequestFactory(HttpHost host, @Nullable String userName, @Nullable String password) {
        super();
        this.host = host;
        this.userName = userName;
        this.password = password;
    }

    public AuthHttpComponentsClientHttpRequestFactory(HttpClient httpClient, HttpHost host) {
        this(httpClient, host, null, null);
    }

    public AuthHttpComponentsClientHttpRequestFactory(HttpClient httpClient, HttpHost host, 
            @Nullable String userName, @Nullable String password) {
        super(httpClient);
        this.host = host;
        this.userName = userName;
        this.password = password;
    }

    @Override
    protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
       // Create AuthCache instance
        AuthCache authCache = new BasicAuthCache();
        // Generate BASIC scheme object and add it to the local auth cache
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);

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

        if (userName != null) {
            BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(new AuthScope(host), new UsernamePasswordCredentials(userName, password));
            localcontext.setCredentialsProvider(credsProvider);
        }
        return localcontext;        
    }

}

It is here – in the creation of the HttpContext – that the basic authentication support is built in. As you can see, doing preemptive Basic Authentication with HttpClient 4.x is a bit of a burden: the authentication info is cached and the process of setting up this authentication cache is very manual and unintuitive.

And with that, everything is in place – the RestTemplate will now be able to support the Basic Authentication scheme; a simple usage pattern would be:

final AuthHttpComponentsClientHttpRequestFactory requestFactory =
    new AuthHttpComponentsClientHttpRequestFactory(
                httpClient, host, userName, password);
final RestTemplate restTemplate = new RestTemplate(requestFactory);

And the request:

restTemplate.get(
    "http://localhost:8080/spring-security-rest-template/api/foos/1",
    Foo.class);

For an in depth discussion on how to secure the REST Service itself, check out this article.

like image 29
Anton Kurniawan Avatar answered Oct 17 '22 01:10

Anton Kurniawan


Since Spring 4.3.1 there is a simplier way using BasicAuthorizationInterceptor, which is also independent of underlying http client used in RestTemplate.

The example that uses RestTemplateBuilder from spring-boot to add BasicAuthorizationInterceptor to RestTemplate:

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate myRestTemplate(RestTemplateBuilder builder) {
        return builder
                .rootUri("http://my.cool.domain/api/")
                .basicAuthorization("login", "password")
                .build();
    }

}

This way any request sent using myRestTemplate bean instance will include given basic authorization header. So be careful to not use the same RestTemplate bean instance to send requests to foreign domains. The rootUri is partially protects from this, but you can always pass the absolute URL when making the request using RestTemplate instance, so be careful!

If you are not using spring-boot, you can also manually add this interceptor to your RestTemplate following this answer.

like image 25
Ruslan Stelmachenko Avatar answered Oct 17 '22 01:10

Ruslan Stelmachenko


If you prefer simple over complex, then just set the header

    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Basic " + Base64.getUrlEncoder().encodeToString("myuser:mypass".getBytes(Charset.forName("UTF-8"))));
    HttpEntity<SomeBody> myRequest = new HttpEntity<>(mybody, headers);
    restTemplate.postForEntity(someUrl, myRequest, null);

I'm sure there's some other Base64-library out there if the encoding that ships with the JDK is too verbose for you.

like image 28
gogstad Avatar answered Oct 17 '22 00:10

gogstad


Why not check the Spring 4 APIs to see which classes implement the required interface, namely ClientHttpRequestFactory?

As you'll see from the Javadoc, most likely you want HttpComponentsClientHttpRequestFactory, which uses the client from Apache's HttpComponents, the successor to the old commons HttpClient.

like image 45
Shaun the Sheep Avatar answered Oct 17 '22 00:10

Shaun the Sheep