Spring RestTemplate получает 401 Unauthorized

Я использую следующее для получения JSON через RestTemplate в Spring 4:

protected DocInfoResponse retrieveData(String urlWithAuth) {
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization", "Basic " + auth.getSig());
    HttpEntity<String> request = new HttpEntity<String>(headers);
    ResponseEntity<DocInfoResponse> response = restTemplate.exchange(urlWithAuth, HttpMethod.GET, request, DocInfoResponse.class);
    return response.getBody();
}

Я использовал тот же код (с другим классом ответа), чтобы успешно получить документ JSON с того же сайта (с разными параметрами, чтобы получить другой документ).

Когда я выполняю приведенный выше код, я получаю следующую трассировку стека (частично):

Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized 
at 
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]

Может ли кто-нибудь указать мне, почему это может быть исключением?


person Squigglylot    schedule 11.03.2017    source источник
comment
Вы пробовали получить доступ к тому же из браузера или почтальона? Он там работает с этой аутентификацией?   -  person Coder    schedule 14.03.2017
comment
Да, я получил тот же URL-адрес, чтобы успешно возвращать ожидаемые результаты в браузерах.   -  person Squigglylot    schedule 16.03.2017
comment
Это определенно проблема аутентификации. Вы создаете цифровые подписи для аутентификации? Указан ли URL-адрес подписи?   -  person Coder    schedule 16.03.2017
comment
Цифровая подпись создается. Это в вызове auth.getSig (). В документации по сайту нет ничего, что указывало бы на привязку к URL-адресу. В обоих случаях используется одна и та же подпись / алгоритм (URL-адрес, который работает, и тот, который не работает).   -  person Squigglylot    schedule 16.03.2017
comment
Не могли бы вы рассказать о другом методе, который работает?   -  person Coder    schedule 16.03.2017
comment
Это точно так же, за двумя следующими исключениями: 1) URL-адрес, 2) объект ответа (SyncResponse.class vs DocInfoResponse.class - первый работает, второй - проблема).   -  person Squigglylot    schedule 16.03.2017
comment
Некоторые цифровые подписи привязаны к URL-адресу. При этом resttemplate кодирует URL-адрес, что немного затрудняет использование цифровой подписи, которая в большинстве случаев относится к URL-адресу. Я не уверен, как auth.getSig() сгенерировала цифровую подпись, поскольку в большинстве случаев она проверяется на основании информации о клиенте, указанной в URL-адресе.   -  person Coder    schedule 17.03.2017
comment
Привет, у меня такая же проблема. Я могу подключиться к целевому URL-адресу с помощью FireFox RESTClient в базовой аутентификации. Однако я получаю 401 Unauthorized, пока использую Spring RestTemplate   -  person avi.elkharrat    schedule 21.07.2017


Ответы (3)


Я обнаружил, что моя проблема, первоначально опубликованная выше, связана с двойным шифрованием параметров auth. Я решил это, используя UriComponentsBuilder и явно вызвав encode() в exchange().

SyncResponse retrieveData(UriComponentsBuilder builder) {
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
    HttpEntity<String> request = new HttpEntity<String>(headers);
    ResponseEntity<SyncResponse> response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, request, SyncResponse.class);
    return response.getBody();
} 

Мой UriComponentsBuilder был построен с использованием:

UriComponentsBuilder buildUrl(String urlString) {
    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(urlString);

    return auth.appendAuth(builder);
}

(auth.appendAuth() добавляет дополнительные .queryParams(), необходимые целевой службе в urlString.)

Призыв выполнить это было retrieveData(buildUrl(urlString));.

person Squigglylot    schedule 24.07.2017

Изучив свою проблему, я понял, что FireFox RESTClient был успешным, потому что я был подключен к целевому URL-адресу. Базовая аутентификация, которую я использовал, в конце концов, была не такой уж простой.

В конце концов, я прочитал документ приложения, к которому пытался подключиться, и понял, что они предлагают механизм токена подключения. Теперь это работает.

Прочитав ваш код, я говорю, что он выглядит вполне нормально, хотя я не уверен, какой у вас объект auth, для которого вы вызываете getSig.

Перво-наперво: попробуйте получить доступ к своей службе из любого клиента, например веб-браузера, PostMan или RESTClient. Убедитесь, что вы успешно получаете информацию БЕЗ подключения к вашему приложению !!!

В зависимости от результата, я говорю, вам следует либо попытаться вручную зашифровать свой Authorization токен (вы легко найдете сообщения на этом сайте, чтобы показать вам, как это сделать), либо попробуйте другой механизм подключения.

person avi.elkharrat    schedule 21.07.2017
comment
В моем случае строка авторизации была дважды зашифрована. Я решил эту проблему, сделав что-то похожее на ваше предложение о ручном шифровании ... с помощью UriComponentsBuilder. - person Squigglylot; 24.07.2017
comment
рад, что это помогло! ваш вопрос тоже был полезен :) - person avi.elkharrat; 26.07.2017

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

 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 restTemplate = new RestTemplate();    
restTemplate.exchange
     (uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);

https://www.baeldung.com/how-to-use-resttemplate-with-basic-authentication-in-spring#manual_auth

person Andrii    schedule 29.02.2020