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 Виталий Куценко расскажет, как избежать нескольких ошибок, которые могут сильно усложнить развитие приложения. Регистрация по ссылке.

Комментарии (1)


  1. ArchDemon
    16.03.2022 08:17

    А как же RestTemplateBuilder (конечно, если мы берём spring boot)?