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;