web-dev-qa-db-fra.com

Authentification de base de Spring 4.0.0 avec RestTemplate

Je travaille actuellement sur l'intégration d'une application tierce avec notre système de reporting local. Je voudrais implémenter REST appels avec authentification de base mais confrontés à des problèmes dans Spring 4.0.0. J'ai une solution simple qui fonctionne bien:

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();

mais je voulais réécrire ceci pour utiliser ClientHttpRequestFactory de la manière suivante:

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);
}

Ce code ne se compile pas car la classe CommonsClientHttpRequestFactory n'existe plus dans Spring 4.0.0. Quelqu'un connaît-il une solution alternative à cela? Je suis assez nouveau dans ce monde REST donc toute aide sera appréciée.

18
shippi

Pourquoi ne pas vérifier les API Spring 4 pour voir quelles classes implémentent l'interface requise, à savoir ClientHttpRequestFactory ?

Comme vous le verrez dans le Javadoc, vous voulez probablement HttpComponentsClientHttpRequestFactory , qui utilise le client des HttpComponents d'Apache, le successeur des anciens communs HttpClient.

4
Shaun the Sheep

Je sais que c'est une vieille question, mais je cherchais moi-même la réponse. Vous devez ajouter un intercepteur RestTemplate lors de la configuration du RestTemplate. Un exemple ci-dessous dans la configuration d'annotation:

@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 pour BasicAuthorizationInterceptor .

J'étais coincé là-dessus pendant quelques bonnes heures. Peut-être que cela aidera quelqu'un dans un avenir proche.

34
Shiraaz.M

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

Spring 3.0, 3.1 et maintenant 4.x prennent en charge très bien les bibliothèques HTTP Apache :

  1. Spring 3.0 , le CommonsClientHttpRequestFactory intégré à la fin de HttpClient 3.x maintenant en vie
  2. Spring 3.1 a introduit la prise en charge du HttpClient 4.x actuel via HttpComponentsClientHttpRequestFactory (prise en charge ajoutée dans le JIRA SPR- 618 )
  3. Spring 4.0 a introduit le support asynchrone via le HttpComponentsAsyncClientHttpRequestFactory

Commençons par configurer les choses avec HttpClient 4 et Spring 4.

RestTemplate nécessitera une fabrique de requêtes HTTP - une fabrique qui prend en charge l'authentification de base - jusqu'à présent, tout va bien. Cependant, l'utilisation directe du HttpComponentsClientHttpRequestFactory existant sera difficile, car l'architecture de RestTemplate a été conçue sans un bon support pour HttpContext - un instrument pièce du puzzle. Nous devons donc sous-classer HttpComponentsClientHttpRequestFactory et remplacer la méthode createHttpContext: ( tiré de soluvas-framework sur 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;        
    }

}

C'est ici - dans la création de HttpContext - que le support d'authentification de base est intégré. Comme vous pouvez le voir, faire une authentification de base préemptive avec HttpClient 4.x est n peu lourd: les informations d'authentification sont mises en cache et le processus de la configuration de ce cache d'authentification est très manuel et peu intuitif .

Et avec cela, tout est en place - le RestTemplate sera désormais en mesure de prendre en charge le schéma d'authentification de base ; un modèle d'utilisation simple serait:

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

Et la demande:

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

Pour une discussion approfondie sur la façon de sécuriser le service REST lui-même, consultez cet article .

18
Anton Kurniawan

Depuis Spring 4.3.1, il existe un moyen plus simple d'utiliser BasicAuthorizationInterceptor , qui est également indépendant du client http sous-jacent utilisé dans RestTemplate.

L'exemple qui utilise RestTemplateBuilder de spring-boot pour ajouter BasicAuthorizationInterceptor à RestTemplate:

@Configuration
public class AppConfig {

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

}

De cette façon toute demande envoyée à l'aide de l'instance de bean myRestTemplate inclura l'en-tête d'autorisation de base donné. Veillez donc à ne pas utiliser la même instance de bean RestTemplate pour envoyer des requêtes à des domaines étrangers. rootUri est partiellement protégé contre cela, mais vous pouvez toujours transmettre l'URL absolue lors de la demande en utilisant l'instance RestTemplate, alors soyez prudent!

Si vous n'utilisez pas spring-boot, vous pouvez également ajouter manuellement cet intercepteur à votre RestTemplate après cette réponse.

6
Ruslan Stelmachenko

Si vous préférez simple plutôt que complexe, définissez simplement l'en-tête

    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);

Je suis sûr qu'il existe une autre bibliothèque Base64 si l'encodage fourni avec le JDK est trop détaillé pour vous.

5
gogstad

J'ai une autre solution pour définir l'authentification de base pour le modèle de repos personnalisé.

RestTemplate restTemplate = new RestTemplate();
    HttpHost proxy =null;
    RequestConfig config=null;
    String credentials = this.env.getProperty("uname") + ":" + this.env.getProperty("pwd");
    String encodedAuthorization = Base64.getEncoder().encodeToString(credentials.getBytes());

    Header header = new BasicHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedAuthorization);
    List<Header> headers = new ArrayList<>();
    headers.add(header);
    // if we need proxy
    if(Boolean.valueOf(env.getProperty("proxyFlag"))){
        proxy = new HttpHost(this.env.getProperty("proxyHost"), Integer.parseInt(env.getProperty("proxyPort")), "http");
        config= RequestConfig.custom().setProxy(proxy).build();
    }else{
        config= RequestConfig.custom().build();
    }


    CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config)
            .setDefaultHeaders(headers).build();

    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
    restTemplate.setRequestFactory(factory);

    return restTemplate;
0
Yalamanda