Недостижимый контекст безопасности с использованием Feign RequestInterceptor

Цель состоит в том, чтобы прикрепить некоторые данные из контекста безопасности с помощью RequestInterceptor, но проблема в том, что вызов SecurityContextHolder.getContext().getAuthentication() всегда возвращает значение null, даже если оно не равно нулю (я уверен на 100%).

Насколько я понимаю, это потому, что Interceptor создается и запускается в другом потоке.

Как я могу решить эту проблему и получить фактические данные из контекста безопасности?

Мой сервис:

@FeignClient(value = "api", configuration = { FeignConfig.class })
public interface DocumentService {

    @RequestMapping(value = "/list", method = RequestMethod.GET)
     DocumentListOperation list();
 }

Мой класс FeignConfig:

@Bean
public RequestInterceptor requestInterceptor() {
    return new HeaderInterceptor(userService);
}

public class HeaderInterceptor implements RequestInterceptor {

    private UserService userService;

    public HeaderInterceptor(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void apply(RequestTemplate requestTemplate) {
        Authentication a = SecurityContextHolder.getContext().getAuthentication()

        requestTemplate.header("authentication", a.toString());
    }
}

person Domas Mar    schedule 11.01.2016    source источник


Ответы (1)


Мне удалось разобраться благодаря статье, которую я нашел здесь

Во-первых, вам нужно инициализировать HystrixRequestContext HystrixRequestContext.initializeContext();.

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

Вот пример:

public class UserHystrixRequestContext {

    private static final HystrixRequestVariableDefault<User> userContextVariable = new HystrixRequestVariableDefault<>();

    private UserHystrixRequestContext() {}

    public static HystrixRequestVariableDefault<User> getInstance() {
        return userContextVariable;
    }
}

Вы должны зарегистрировать новую стратегию параллелизма, которая будет обертывать интерфейс Callable

@Component
public class CustomHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

    public CustomHystrixConcurrencyStrategy() {
        HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
    }

    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        return new HystrixContextWrapper<T>(callable);
    }

    public static class HystrixContextWrapper<V> implements Callable<V> {

        private HystrixRequestContext hystrixRequestContext;
        private Callable<V> delegate;

        public HystrixContextWrapper(Callable<V> delegate) {
        this.hystrixRequestContext = HystrixRequestContext.getContextForCurrentThread();
            this.delegate = delegate;
        }

        @Override
        public V call() throws Exception {
            HystrixRequestContext existingState = HystrixRequestContext.getContextForCurrentThread();
            try {
                HystrixRequestContext.setContextOnCurrentThread(this.hystrixRequestContext);
                return this.delegate.call();
            } finally {
                HystrixRequestContext.setContextOnCurrentThread(existingState);
            }
        }
    }
}

Поэтому перед вызовом вызываемого объекта мы устанавливаем контекст нового потока в контекст родителя.

После этого вы сможете получить доступ к новому определенному контексту внутри дочерних потоков Hystrix.

User = UserHystrixRequestContext.getInstance().get();

Надеюсь, это поможет кому-то.

person Domas Mar    schedule 15.01.2016
comment
Спасибо за этот ответ. Действительно помог мне. Я думаю, что вы можете просто использовать фильтр сервлетов - так что нет необходимости реализовывать CustomHystrixConcurrencyStrategy? - person Tony Murphy; 07.03.2021