Spring 4.0.0 базовая аутентификация с помощью RestTemplate

В настоящее время я работаю над интеграцией стороннего приложения с нашей локальной системой отчетности. Я хотел бы использовать вызовы REST с базовой аутентификацией, но сталкиваясь с проблемами в Spring 4.0.0. У меня есть простое решение, которое прекрасно работает:

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

но хотел переписать это, чтобы использовать ClientHttpRequestFactory следующим образом:

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

Этот код не компилируется, поскольку класс CommonsClientHttpRequestFactory больше не существует в Spring 4.0.0. Кто-нибудь знает какое-нибудь альтернативное решение? Я совершенно новый в этом мире REST, поэтому любая помощь будет оценена.

Ответы

Ответ 1

Почему бы не проверить API Spring 4, чтобы увидеть, какие классы реализуют требуемый интерфейс, а именно ClientHttpRequestFactory?

Как вы увидите из Javadoc, скорее всего, вы хотите HttpComponentsClientHttpRequestFactory, который использует клиента из Apache HttpComponents, преемника к старым достоянию HttpClient.

Ответ 2

Я знаю, что это старый вопрос, но я сам искал ответ. При настройке RestTemplate вам нужно добавить перехватчик RestTemplate. Пример ниже в конфигурации аннотации:

@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 для BasicAuthorizationInterceptor.

Я застрял на этом несколько часов. Возможно, это поможет кому-то в ближайшем будущем.

Ответ 3

От http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1/ с изменениями HttpClient 4.3:

Оба Spring 3.0 и 3.1 и теперь 4.x имеют очень хорошую поддержку Apache HTTP-библиотек:

  • Spring 3.0, CommonsClientHttpRequestFactory, интегрированный с теперь завершенным HttpClient 3.x
  • Spring 3.1 появилась поддержка текущего HttpClient 4.x через HttpComponentsClientHttpRequestFactory (поддержка добавлена ​​в JIRA SPR-6180)
  • Spring 4.0 представила поддержку async через HttpComponentsAsyncClientHttpRequestFactory

Давайте начнем настройку с помощью HttpClient 4 и Spring 4.

Для RestTemplate потребуется HTTP-запрос factory - factory, который поддерживает Базовая аутентификация - пока что так хорошо. Однако использование существующего HttpComponentsClientHttpRequestFactory будет затруднительным, так как архитектура RestTemplate была разработана без хорошей поддержки для HttpContext - инструментальной части головоломка. И так нужно подклассом HttpComponentsClientHttpRequestFactory и переопределить метод createHttpContext: (взято из soluvas-framework на 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;        
    }

}

Именно здесь - при создании HttpContext - встроена базовая поддержка аутентификации. Как вы можете видеть, выполнение превентивной базовой проверки подлинности с помощью HttpClient 4.x является немного бремени: информация аутентификации кэшируется, и процесс настройки этого кеша проверки очень ручен и неинтуитивен.

И с этим все будет на месте - теперь RestTemplate сможет поддерживать схему Базовая аутентификация; простой шаблон использования:

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

И запрос:

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

Подробное обсуждение того, как защитить службу REST, проверить эту статью.

Ответ 4

Если вы предпочитаете простой сложный, просто установите заголовок

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

Я уверен, что там есть другая Base64-библиотека, если кодировка, поставляемая с JDK, слишком многословна для вас.

Ответ 5

Так как Spring 4.3.1 существует более простой способ использования BasicAuthorizationInterceptor, который также не зависит от основного http-клиента, используемого в RestTemplate.

Пример, который использует RestTemplateBuilder из spring-boot, чтобы добавить BasicAuthorizationInterceptor в RestTemplate:

@Configuration
public class AppConfig {

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

}

Таким образом любой запрос, отправленный с использованием экземпляра myRestTemplate bean, будет содержать указанный основной заголовок авторизации. Поэтому будьте осторожны, чтобы не использовать один и тот же экземпляр RestTemplate bean для отправки запросов в чужие домены. rootUri частично защищает от этого, но вы всегда можете передать абсолютный URL-адрес при выполнении запроса с использованием экземпляра RestTemplate, поэтому будьте осторожны!

Если вы не используете spring-boot, вы также можете вручную добавить этот перехватчик в свой RestTemplate после этого ответа.

Ответ 6

У меня есть другое решение для установки базовой аутентификации для настроенного шаблона отдыха.

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;