Надсилання запиту GET із заголовками автентифікації за допомогою restTemplate


76

Мені потрібно отримати ресурси зі свого сервера, надіславши запит GET із деякими заголовками авторизації за допомогою RestTemplate.

Ознайомившись із документами, я помітив, що жоден із методів GET не приймає заголовки як параметр, і єдиним способом надсилання таких заголовків, як accept і Authorization, є метод обміну .

Оскільки це дуже елементарна дія, мені цікаво, чи щось мені не вистачає, і є інший, простіший спосіб зробити це?

Відповіді:


66

Ви нічого не пропускаєте. RestTemplate#exchange(..)є відповідним методом для встановлення заголовків запитів.

Ось приклад (з POST, але просто змініть його на GET і використовуйте потрібну сутність).

Ось ще один приклад.

Зверніть увагу, що за допомогою GET сутність запиту не повинна містити нічого (якщо ваш API цього не очікує, але це суперечить специфікації HTTP). Це може бути порожній рядок.


1
Але що, якби ви хотіли, щоб ваш метод отримання запиту зв’язав об’єкт, але все ж хотів надіслати заголовки?
user2725919

50

Ви можете використовувати postForObjectз HttpEntity. Це виглядало б так:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);

HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String result = restTemplate.postForObject(url, entity, String.class);

У запиті GET ви зазвичай не надсилаєте тіло (це дозволено, але це не має жодної мети). Спосіб додавання заголовків без підключення RestTemplate по-іншому полягає у безпосередньому використанні методів exchangeабо execute. Скорочені стилі отримання не підтримують модифікацію заголовка.

Асиметрія на перший погляд трохи дивна, можливо, це буде виправлено у майбутніх версіях Spring.


4
restTemplate.postForEntity(url, entity, String.class)також чудово працює.
Абдулл

Питання стосується запиту GET, але ця відповідь стосується POST. Це оманливе. Немає getForObjectтакого підпису.
Zon

26

Ось надзвичайно простий приклад з базовою автентифікацією, заголовками та обробкою винятків ...

private HttpHeaders createHttpHeaders(String user, String password)
{
    String notEncoded = user + ":" + password;
    String encodedAuth = "Basic " + Base64.getEncoder().encodeToString(notEncoded.getBytes());
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.add("Authorization", encodedAuth);
    return headers;
}

private void doYourThing() 
{
    String theUrl = "http://blah.blah.com:8080/rest/api/blah";
    RestTemplate restTemplate = new RestTemplate();
    try {
        HttpHeaders headers = createHttpHeaders("fred","1234");
        HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
        ResponseEntity<String> response = restTemplate.exchange(theUrl, HttpMethod.GET, entity, String.class);
        System.out.println("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
    }
    catch (Exception eek) {
        System.out.println("** Exception: "+ eek.getMessage());
    }
}

2
Не впевнений, чому спочатку це було проти. Якщо ви перший, хто проголосував проти, сміливо пояснюйте у коментарі.
Сандер Верхаген

2
Частина "Основна" не повинна кодуватися, чи не так?
Веннегуен,

1
Метод createHttpHeaders дещо неправильний. Рядок notEncoded = user + ":" + пароль; ... headers.add ("Авторизація", "Основний" + encodedAuth);
Milind

Я також щойно виявив, що "Basic" також не слід кодувати. Це хороший приклад того, як робити аутентифікацію, яку я сам використовував, але коригування кодування було б непогано.
Тім Холт,

Виправлено базове кодування, згадане у коментарях вище
Адріан Костер

10

Усі ці відповіді видаються неповними та / або наганяють. Дивлячись на інтерфейс RestTemplate, здається, він передбачає ClientHttpRequestFactoryвведення в нього, а потім цей requestFactory буде використаний для створення запиту, включаючи будь-які налаштування заголовків, тексту та параметрів запиту.

Вам потрібен універсал ClientHttpRequestFactoryдля введення в один спільний ресурс, RestTemplateабо ж вам потрібно отримати новий екземпляр шаблону через new RestTemplate(myHttpRequestFactory).

На жаль, створювати таку фабрику виглядає дещо нетривіально, навіть коли ви просто хочете встановити єдиний заголовок авторизації, що досить неприємно, враховуючи, яка загальна вимога, яка, ймовірно, є, але принаймні це дозволяє легко використовувати, якщо, наприклад , ваш заголовок Authorization можна створити з даних, що містяться в Authorizationоб'єкті Spring-Security , тоді ви можете створити фабрику, яка встановлює вихідний AuthorizationHeader для кожного запиту, виконуючи, SecurityContextHolder.getContext().getAuthorization()а потім заповнюючи заголовок, з необхідними перевірками. Тепер усі вихідні дзвінки відпочинку, зроблені за допомогою цього RestTemplate, матимуть правильний заголовок авторизації.

Без особливого акценту на механізмі HttpClientFactory, що забезпечує прості для перевантаження базові класи для таких звичайних випадків, як додавання одного заголовка до запитів, більшість приємних зручних методів в RestTemplateкінцевому підсумку виявляються марною тратою часу, оскільки їх можна використовувати лише рідко .

Я хотів би побачити щось таке, як це

@Configuration
public class MyConfig {
  @Bean
  public RestTemplate getRestTemplate() {
    return new RestTemplate(new AbstractHeaderRewritingHttpClientFactory() {
        @Override
        public HttpHeaders modifyHeaders(HttpHeaders headers) {
          headers.addHeader("Authorization", computeAuthString());
          return headers;
        }
        public String computeAuthString() {
          // do something better than this, but you get the idea
          return SecurityContextHolder.getContext().getAuthorization().getCredential();
        }
    });
  }
}

На даний момент з інтерфейсом доступних ClientHttpRequestFactory важче взаємодіяти, ніж із цим. Ще кращою була б абстрактна обгортка для існуючих заводських реалізацій, яка робить їх схожими на простіший об'єкт, такий як AbstractHeaderRewritingRequestFactory, для цілей заміни лише однієї частини функціональності. Зараз вони мають дуже загальне призначення, так що навіть написання цих обгортків є складним дослідженням.


2
Хоча ваша "відповідь" дуже цікава, вона швидше нагадує коментар, ніж фактичну відповідь.
Мартін Шредер,

3

У ці дні вистачить чогось наступного:

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
restTemplate.exchange(RequestEntity.get(new URI(url)).headers(headers).build(), returnType);

0

Простим рішенням було б налаштувати статичні заголовки http, необхідні для всіх викликів у конфігурації компонента RestTemplate:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate(@Value("${did-service.bearer-token}") String bearerToken) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add((request, body, clientHttpRequestExecution) -> {
            HttpHeaders headers = request.getHeaders();
            if (!headers.containsKey("Authorization")) {
                String token = bearerToken.toLowerCase().startsWith("bearer") ? bearerToken : "Bearer " + bearerToken;
                request.getHeaders().add("Authorization", token);
            }
            return clientHttpRequestExecution.execute(request, body);
        });
        return restTemplate;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.