修正了feign开启熔断器后无法获得 requestContext 的问题。

This commit is contained in:
ajaxfan
2020-12-30 19:20:25 +08:00
parent 0fa0e73342
commit d692b29768
5 changed files with 254 additions and 28 deletions

View File

@ -8,7 +8,7 @@
<groupId>com.chinaunicom.ebtp</groupId>
<artifactId>mall-ebtp-cloud-parent</artifactId>
<version>0.0.1</version>
<relativePath>../mall-ebtp-cloud-parent</relativePath>
<relativePath />
</parent>
<groupId>com.chinaunicom.ebtp</groupId>
<artifactId>mall-ebtp-cloud-feign-starter</artifactId>
@ -20,6 +20,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>

View File

@ -1,10 +1,12 @@
package com.chinaunicom.mall.ebtp.cloud.feign.starter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:feign-configuration.properties")
@ComponentScan(basePackages = "com.chinaunicom.mall.ebtp.cloud.feign.starter")
public class FeignStarterConfiguration {
}

View File

@ -0,0 +1,57 @@
package com.chinaunicom.mall.ebtp.cloud.feign.starter;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.hystrix.security.SecurityContextConcurrencyStrategy;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import com.chinaunicom.mall.ebtp.cloud.feign.starter.HystrixSecurityAutoConfiguration.HystrixSecurityCondition;
import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
@Configuration
@Conditional(HystrixSecurityCondition.class)
@ConditionalOnClass({ Hystrix.class })
public class HystrixSecurityAutoConfiguration {
private @Autowired HystrixConcurrencyStrategy requestAttributeHystrixConcurrencyStrategy;
@PostConstruct
public void init() {
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(
new SecurityContextConcurrencyStrategy(requestAttributeHystrixConcurrencyStrategy));
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
}
static class HystrixSecurityCondition extends AllNestedConditions {
public HystrixSecurityCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = "hystrix.shareSecurityContext")
static class ShareSecurityContext {
}
}
}

View File

@ -0,0 +1,163 @@
package com.chinaunicom.mall.ebtp.cloud.feign.starter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
/**
* 为了解决熔断器开启后, RequestInterceptor无法注入header的问题 ( Hystrix有隔离策略THREAD以 及
* SEMAPHORE, thread策略会导致SecurityContext及RequestContext无法获取当前上下文 因其实线程隔离的 )
*
* @author Ajaxfan
*/
@Slf4j
@Component("requestAttributeHystrixConcurrencyStrategy")
public class RequestAttributeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private HystrixConcurrencyStrategy delegate;
public RequestAttributeHystrixConcurrencyStrategy() {
init();
}
/**
* @param <T>
* @param callable
* @return
*/
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new WrappedCallable<>(callable, RequestContextHolder.getRequestAttributes());
}
/**
* @param threadPoolKey
* @param corePoolSize
* @param maximumPoolSize
* @param keepAliveTime
* @param unit
* @param workQueue
* @return
*/
@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);
}
/**
* @param threadPoolKey
* @param threadPoolProperties
* @return
*/
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
}
/**
* @param maxQueueSize
* @return
*/
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
/**
* @param <T>
* @param rv
* @return
*/
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
/**
* @author Ajaxfan
*
* @param <T>
*/
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();
}
}
}
/**
* 初始化隔离器
*/
private void init() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof RequestAttributeHystrixConcurrencyStrategy) {
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);
}
}
/**
* 生产当前隔离参数日志
*
* @param eventNotifier
* @param metricsPublisher
* @param propertiesStrategy
*/
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy [" + this.delegate + "],"
+ "eventNotifier [" + eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"
+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}

View File

@ -1,9 +1,13 @@
package com.chinaunicom.mall.ebtp.common.config;
import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.AUTHORIZATION_HEADER;
import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.TOKEN_PREFIX;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import feign.RequestInterceptor;
import feign.RequestTemplate;
@ -14,23 +18,19 @@ import feign.RequestTemplate;
@Configuration
public class FeignConfig implements RequestInterceptor {
private final String AUTHORIZATION_HEADER = "Authorization";
private final String BEARER_TOKEN_TYPE = "Bearer";
@Override
public void apply(RequestTemplate template) {
// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// if (null != attributes) {
// HttpServletRequest request = attributes.getRequest();
// String token = request.getHeader("JwtToken");
// template.header("JwtToken", token);
// }
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (authentication != null && authentication.getCredentials() instanceof String) {
String token = (String) authentication.getCredentials();
template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, token));
if (null != attributes) {
final String header = attributes.getRequest().getHeader(AUTHORIZATION_HEADER);// 提取request头信息
// 检查请求头是否包含 Bearer 前缀
if (StringUtils.startsWith(header, TOKEN_PREFIX)) {
String authToken = RegExUtils.replaceAll(header, TOKEN_PREFIX, "");// 提取 token 信息
template.header(AUTHORIZATION_HEADER, String.format("%s %s", TOKEN_PREFIX, authToken));
}
}
}