Использование Basic Authentication с RestTemplate в Spring

от автора

1. Обзор

В этой статье рассмотрим, как использовать Spring’овый RestTemplate для работы с RESTful-сервисами, защищенными Basic Authentication.

После настройки RestTemplate для работы с Basic Authentication все запросы будут содержать учетные данные, необходимые для выполнения процесса аутентификации. Данные для аутентификации кодируются и записываются в HTTP-заголовок Authorization, который выглядит следующим образом:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

2. Настройка RestTemplate

Для получения RestTemplate в контексте Spring, достаточно объявить его как бин. Но Basic Authentication требует ручной конфигурации, поэтому будем использовать FactoryBean:

@Component public class RestTemplateFactory   implements FactoryBean<RestTemplate>, InitializingBean {       private RestTemplate restTemplate;      public RestTemplate getObject() {         return restTemplate;     }     public Class<RestTemplate> getObjectType() {         return RestTemplate.class;     }     public boolean isSingleton() {         return true;     }      public void afterPropertiesSet() {         HttpHost host = new HttpHost("localhost", 8082, "http");         restTemplate = new RestTemplate(           new HttpComponentsClientHttpRequestFactoryBasicAuth(host));     } }

Параметры host и port обычно зависят от окружения: у клиента должна быть возможность определять один набор значений, например, для интеграционного тестирования, а другой для продакшена. Эти значения можно задавать через файлы свойств.

3. Ручное управление HTTP-заголовком Authorization

Заголовок Authorization можно добавить вручную:

HttpHeaders createHeaders(String username, String password){    return new HttpHeaders() {{          String auth = username + ":" + password;          byte[] encodedAuth = Base64.encodeBase64(              auth.getBytes(Charset.forName("US-ASCII")) );          String authHeader = "Basic " + new String( encodedAuth );          set( "Authorization", authHeader );       }}; }

Отправить запрос также просто:

restTemplate.exchange  (uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);

4. Автоматическое управление HTTP-заголовком Authorization 

В 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 появилась поддержка асинхронности через HttpComponentsAsyncClientHttpRequestFactory.

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

Для RestTemplate потребуется фабрика HTTP-запросов, поддерживающая Basic Authentication. Однако напрямую использовать существующий HttpComponentsClientHttpRequestFactory непросто, поскольку RestTemplate не очень хорошо поддерживает HttpContext — важной части решения. Поэтому нам понадобится создать подкласс HttpComponentsClientHttpRequestFactory и переопределить метод createHttpContext:

public class HttpComponentsClientHttpRequestFactoryBasicAuth    extends HttpComponentsClientHttpRequestFactory {      HttpHost host;      public HttpComponentsClientHttpRequestFactoryBasicAuth(HttpHost host) {         super();         this.host = host;     }      protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {         return createHttpContext();     }          private HttpContext createHttpContext() {         AuthCache authCache = new BasicAuthCache();          BasicScheme basicAuth = new BasicScheme();         authCache.put(host, basicAuth);          BasicHttpContext localcontext = new BasicHttpContext();         localcontext.setAttribute(HttpClientContext.AUTH_CACHE, authCache);         return localcontext;     } }

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

Далее просто добавляем BasicAuthorizationInterceptor в RestTemplate:

restTemplate.getInterceptors().add(   new BasicAuthorizationInterceptor("username", "password"));

Выполняем запрос:

restTemplate.exchange(   "http://localhost:8082/spring-security-rest-basic-auth/api/foos/1",    HttpMethod.GET, null, Foo.class);

Подробнее о том, как обеспечить безопасность самого REST-сервиса читайте в этой статье.

5. Зависимости Maven

Нам потребуются зависимости Maven для самого RestTemplate и библиотеки HttpClient:

<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-webmvc</artifactId>    <version>5.0.6.RELEASE</version> </dependency>  <dependency>    <groupId>org.apache.httpcomponents</groupId>    <artifactId>httpclient</artifactId>    <version>4.5.3</version> </dependency>

Если мы решим создавать HTTP-заголовок Authorization вручную, то нам также потребуется дополнительная библиотека для поддержки кодирования:

<dependency>    <groupId>commons-codec</groupId>    <artifactId>commons-codec</artifactId>    <version>1.10</version> </dependency>

6. Заключение

Большинство информации, которую можно найти по RestTemplate и безопасности, все еще не учитывает текущие релизы HttpClient 4.x, даже несмотря на то, что ветка 3.x устарела и Spring’ом не поддерживается. В этой статье мы немного восполнили этот пробел, описав, как настроить Basic Authentication для RestTemplate, и использовать его для запросов к защищенному REST API.

Полный пример кода с RESTful-сервисом вы можете найти на Github.


Все разработчики проходят одинаковый путь в развитии. Приглашаем всех желающих на demo-занятие «Послание про архитектуру приложений самому себе в прошлое», на котором преподаватель OTUS Виталий Куценко расскажет, как избежать нескольких ошибок, которые могут сильно усложнить развитие приложения. Регистрация по ссылке.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/655739/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *