Как добавить поддержку токена авторизации в REST клиенте Spring
Что проверяют
Знаете ли вы, что токен добавляют один раз в конфигурации клиента, а не руками в каждом вызове, и какой клиент в Spring выбирать сейчас. Следом спрашивают, как быть с истечением токена и как пробросить токен входящего запроса в другой сервис.
Как отвечать
Сначала про выбор клиента. RestTemplate в режиме поддержки, новые возможности получает RestClient (Spring Framework 6.1, Spring Boot 3.2) с синхронным fluent-API. WebClient нужен в реактивном стеке.
Способы добавить токен, от простого к правильному:
- статический заголовок:
RestClient.builder().defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + key). Годится для неизменяемого API-ключа; - интерсептор:
ClientHttpRequestInterceptor, который перед каждым запросом берёт актуальный токен и вызываетrequest.getHeaders().setBearerAuth(token). Подключается черезrequestInterceptor(...)уRestClient.BuilderилиadditionalInterceptors(...)уRestTemplateBuilder; - фильтр для WebClient:
ExchangeFilterFunction, та же идея в реактивном виде; - OAuth2 client credentials: Spring Security берёт получение, кеширование и обновление токена на себя через
OAuth2AuthorizedClientManager. ДляRestClientестьOAuth2ClientHttpRequestInterceptor, дляWebClientфильтрServletOAuth2AuthorizedClientExchangeFilterFunction, а сам клиент описывается вspring.security.oauth2.client.registration.*.
Для проброса токена пользователя интерсептор достаёт JwtAuthenticationToken из SecurityContextHolder и кладёт значение токена в заголовок. На 401 разумно один раз обновить токен и повторить запрос.
Где спотыкаются
Создают клиент через new RestTemplate() внутри сервиса. Так теряются автонастройки Boot, метрики и трассировка; клиент должен быть бином, собранным через билдер.
Хранят токен в изменяемом поле синглтона и перезаписывают его из разных потоков. Нужен потокобезопасный кеш со сроком жизни или готовый OAuth2AuthorizedClientManager.
Логируют заголовки целиком вместе с Authorization или ловят любое исключение клиента и возвращают 500. В первом случае токен утекает в логи, во втором 401 от внешнего API маскируется под внутреннюю ошибку.