子线程内通过feignClient调用服务并携带token
所需解决问题:
1、前端发过来请求,后台requestAttributes仅在当前线程可见;
2、请求结束后,数据销毁!
3、requestAttributes未共享到子线程
介绍:
InheritableThreadLocal数据可在父线程及子线程中共享!
方案一:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
InheritableThreadLocal 中数据在子线程中可见
所有可以将参数 boolean inheritable 赋为true!
注意: 如果使用全局线程池,将任务丢到线程池中,那么此方法将失效。
因为任务丢到线程池后,当前请求的线程不会等待任务执行完毕。请求会退出结束(通过RequestListener可以看到,请求destroy掉了),所以你获取不到header了!
/**
* Bind the given RequestAttributes to the current thread.
* @param attributes the RequestAttributes to expose,
* or {@code null} to reset the thread-bound context
* @param inheritable whether to expose the RequestAttributes as inheritable
* for child threads (using an {@link InheritableThreadLocal})
*/
public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
方案二:
使用threadLocal,将token存在threadLocal中。
封装token获取,释放操作,方便使用者,仅编写业务逻辑!
public abstract class FeignConsumerWithTokenRunnable implements Runnable {
private final static Logger LOGGER = Logger.getLogger(FeignConsumerWithTokenRunnable.class.getName());
private static final ThreadLocal<String> tokenThreadLocal = new ThreadLocal<>();
public abstract void feignConsumerWithToeknTask();
public abstract String getToken();
@Override
public void run(){
try {
tokenThreadLocal.set(getToken());
feignConsumerWithToeknTask();
}catch(Exception e) {
//Todo 打印日志
}finally {
tokenThreadLocal.remove();
}
}
}
public void getDataAsync(@RequestHeader("Authorization") String authToken, Long id) {
taskExecutor.execute(new FeignConsumerWithTokenRunnable() {
@Override
public void feignConsumerWithToeknTask() {
//业务逻辑
downloadService.downloadAsyn(id);
}
@Override
public String getToken() {
return token;
}
});
}
添加配置类,继承RequestInterceptor接口,实现apply方法,从threadLocal中获取token。
@Configuration
public class FeignClientConfig implements RequestInterceptor {
private final static Logger LOGGER = Logger.getLogger(FeignClientConfig.class.getName());
@Override
public void apply(RequestTemplate requestTemplate) {
String token = "";
try {
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
if (requestAttributes != null) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
token = request.getHeader("Authorization");
}
}catch (Exception e) {
token = ReportResource.threadLocal.get();
LOGGER.warning("Get token from requestAttributes failed , successed from threadLocal");
}
if(!StringUtils.isBlank(token))
requestTemplate.header("Authorization", token);
}
}