修正了feign开启熔断器后无法获得 requestContext 的问题。
This commit is contained in:
@ -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>
|
||||
@ -16,14 +16,18 @@
|
||||
<name>mall-ebtp-cloud-feign-starter</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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.");
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
if (authentication != null && authentication.getCredentials() instanceof String) {
|
||||
String token = (String) authentication.getCredentials();
|
||||
template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, token));
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user