Пересылка заголовков запросов на несколько сервисных вызовов в Spring + Netflix + Feign

В моем приложении есть несколько промежуточных и основных сервисов. Все сервисы являются Spring Boot и используют библиотеку Netflix. Когда пользователь запрашивает информацию, запрос будет / может передавать другие службы в цепочке, например:

Client <-> Zuul <-> Service B <-> Service A

Я настроил все службы (A и B) как ResourceServer, так что каждый доступ должен быть аутентифицирован. При запросе токена доступа (от Spring Security Server) и использовании его для запроса информации непосредственно из службы A все работает нормально. Когда я использую тот же токен для доступа к информации из службы B (которой требуется служба A в дальнейшем), я получаю ошибку «HTTP 401: требуется полная аутентификация». Служба B использует FeignClient для вызова службы A.

После некоторой отладки я обнаружил, что заголовок авторизации не передается из службы B в службу A. Служба B правильно проверяет сам токен, предоставляет доступ к методу и пытается выполнить запрос службы A.

Я попробовал RequestInterceptor, но безуспешно (ошибка «Scope 'request' не активна для текущего потока»)

@Component
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_TOKEN_TYPE = "Bearer";
    private final OAuth2ClientContext oauth2ClientContext;

    public OAuth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext) {
        Assert.notNull(oauth2ClientContext, "Context can not be null");
        this.oauth2ClientContext = oauth2ClientContext;
    }

    @Override
    public void apply(RequestTemplate template) {
        if (template.headers().containsKey(AUTHORIZATION_HEADER)) {
            ...
        } else if (oauth2ClientContext.getAccessTokenRequest().getExistingToken() == null) {
            ...
        } else {
            template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE,
                    oauth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));
        }
    }
}

Это пример прокси-функции, использующей FeignClient:

@Autowired
private CategoryClient cat;


@HystrixCommand(fallbackMethod = "getAllFallback", commandProperties = {@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2") })
@GetMapping("/category")
public ResponseEntity<List<Category>> getAll() {
    try {
        ResponseEntity<List<Category>> categories = this.cat.getAll();
        ...
        return categories;
    } catch(Exception e) {
        ...
    }
}

Есть ли какое-либо рабочее решение для передачи заголовка авторизации из прокси-функции в FeignClient, чтобы служба A получила заголовок и могла выполнять с ним собственную проверку аутентификации?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
1 484
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Нашел рабочее решение. Я до сих пор не знаю, является ли это «лучшим» способом сделать это, и если у кого-то есть лучшее решение, я буду рад, если вы поделитесь им. Но пока это работает так, как ожидалось:

@Bean
public RequestInterceptor requestTokenBearerInterceptor() {

    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate requestTemplate) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
            requestTemplate.header("Authorization", "Bearer " + details.getTokenValue());                   
        }
    };
}

Другие вопросы по теме