Feign调用时将请求头信息传递到调用下游方案
一、需求分析
现有接口存在对用户信息进行拦截鉴权需求,在使用Feign包调用下游接口时,需要将当前请求头一直传递下去。
二、实现方案
在参考了网上的各种资料之后,通过自定义hystrix并发策略和Feign调用拦截器实现此需求
1、拦截器定义
拦截全部的Feign调用请求,从当前requestContext请求头中获取出需要的身份信息,再手动添加到requestTemplate中。
这里的问题是,当请求走到此拦截器中时,如果没有配置hystrix并发策略为信号量模式的话,就会根据hystrix默认的并发策略,进入一个新的线程池。所以在这里是获取不到我们想要的requestContext的。需要通过第二步进行处理。
@Configuration
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
// 服务内部跳转时,可能不存在Request上下文
if (Objects.isNull(attributes)){
requestTemplate.header(AuthorizationUtil.Authorization, AuthorizationUtil.Authorization);
return;
}
HttpServletRequest request = attributes.getRequest();
boolean hasParam=(request.getParameter(AuthorizationUtil.Authorization)!=null&& !"".equals(request.getParameter(AuthorizationUtil.Authorization)));
if("GET".equals(request.getMethod())&& hasParam){
requestTemplate.header(AuthorizationUtil.Authorization, request.getParameter(AuthorizationUtil.Authorization));
return;
}
Enumeration<String> headerNames = request.getHeaderNames();
boolean bool = false;
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
// 只取需要的header
if ("Authorization".equals(name) || "authorization".equals(name)){
requestTemplate.header(name, values);
bool = true;
} else if (AuthorizationUtil.Authorization.equals(name)) {
requestTemplate.header(name, values);
bool = true;
}
}
if (!bool) {
//判断url中是否有数据
requestTemplate.header(AuthorizationUtil.Authorization, AuthorizationUtil.Authorization);
}
} else {
requestTemplate.header(AuthorizationUtil.Authorization, AuthorizationUtil.Authorization);
}
}
}
2、自定义hystrix并发策略
重点在于继承HystrixConcurrencyStrategy之后,重写wrapCallable方法,内部把当前上下文中的信息填充到新线程的上下文中
@Configuration
public class RequestHeaderHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final Log log = LogFactory.getLog(RequestHeaderHystrixConcurrencyStrategy.class);
private HystrixConcurrencyStrategy delegate;
public RequestHeaderHystrixConcurrencyStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof RequestHeaderHystrixConcurrencyStrategy) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins
.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
.getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
.getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
.getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher,
propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
HystrixPlugins.getInstance()
.registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
}
catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is ["
+ "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier ["
+ eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"
+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
keepAliveTime, unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(
HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
this.target = target;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return target.call();
}
finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}
3、Feign配置注册
如果是微服务项目,并且是通过引包的方式调用Feign接口的话,需要装配拦截器FeignInterceptor。
@Configuration
public class FeignSupportConfig {
@Bean
public RequestInterceptor requestInterceptor(){
return new FeignInterceptor();
}
}
在项目启动类上加@ComponentScan,配置FeignSupportConfig所在的包路径,使拦截器生效。

浙公网安备 33010602011771号