Включение OAuth2 с Feign для запланированных межсервисных задач

Я пытаюсь решить головоломку, включив аутентификацию на основе OAuth2 для моего клиента Feign, который используется для связи между службами.

В обычных случаях, когда пользователь отправляет запрос через API, я могу взять все данные аутентификации, необходимые для SecurityContextHolder Spring (поскольку он обычно выполняет свою работу и заполняет все объекты деталей) и улучшить Request Feign следующим образом:

public class FeignAccessTokenRelyInterceptor implements RequestInterceptor {
    private static final Logger log = LoggerFactory.getLogger(FeignAccessTokenRelyInterceptor.class);

    @Override
    public void apply(RequestTemplate template) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            String tokenValue = null;
            if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
                OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
                tokenValue = details.getTokenValue();
            }
            if (tokenValue == null) {
                log.warn("Current token value is null");
                return;
            }
            template.header("Authorization", "Bearer " + tokenValue);
        }
    }
}

Однако когда дело доходит до запланированных вызовов, которые инициируются внутри системы, SecurityContext явно пуст. Я заполняю его UserInfoTokenServices, вручную запрашивая токен доступа с помощью потока учетных данных клиента и загружая данные пользователя:

OAuth2Authentication authentication = userInfoTokenServices.loadAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);

Но такая конструкция не заполняет OAuth2Authentication.details, на который я рассчитываю получить токен доступа. Я попытался расширить OAuth2AuthenticationDetails, но для единственного конструктора требуется HttpServletRequest, который трудно внедрить в запланированную задачу, и создание его фиктивного экземпляра кажется плохим выбором.

Пока я вижу единственный адекватный вариант сделать отдельную кастомную реализацию держателя деталей и передать ее OAuth2Authentication вместе с имеющимся у меня токеном доступа. А потом забрать в FeignAccessTokenRelyInterceptor.

Вопрос

Может быть, есть какие-то другие варианты, где я могу хранить свой токен доступа в контексте безопасности и надежно получать его оттуда, чтобы не создавать новые пользовательские классы?

Будем рады любой помощи.

Некоторые связанные ссылки, которые я изучил:


person Vladimir Salin    schedule 29.12.2017    source источник


Ответы (1)


Что касается истории, надеюсь, это поможет кому-то вроде меня бороться с этим.

Я не нашел лучшего способа, чем первоначальный, и сделал собственный InternalOAuth2Details для хранения значения токена, полученного от сервисов Spring OAuth. Затем в FeignAccessTokenRelyInterceptor я просто проверяю, соответствуют ли текущие данные InternalOAuth2Details, и пытаюсь получить из них значение токена следующим образом:

@Override
public void apply(RequestTemplate template) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

    if (auth != null){
        String tokenValue = null;
        if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
            tokenValue = details.getTokenValue();
        } else if (auth.getDetails() instanceof InternalOAuth2Details) {
            InternalOAuth2Details details = (InternalOAuth2Details) auth.getDetails();
            tokenValue = details.getTokenValue();
        }
        if (tokenValue == null) {
            log.warn("Current token value is null");
            return;
        }
        template.header("Authorization", "Bearer " + tokenValue);
    }
}

Бьюсь об заклад, это не лучшее решение, но на данный момент оно работает довольно стабильно.

person Vladimir Salin    schedule 18.01.2018