这种是因为我们在A服务调用B服务时,并不能把当前线程的用户信息带过去,因为SecurityContextHolder.getContext().getAuthentication()取值,是取的自己本地线程的。Feign集成Hystrix默认是关闭Hystrix的,只有在配置文件中设置feign.hystrix.enabled=true才会开启Hystrix。开启Hystrix后feign之间的方法调用就会默认启动新的线程执行,和主程序不在一个线程中,因此如果上下文中存在ThreadLocal变量,在该方法中就失效了。
一、不开启服务降级熔断
可以直接使用SecurityContextHolder.getContext().getAuthentication();获取当前token并传递给下游服务
@Configuration
public class MyOAuthRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof JwtAuthenticationToken){
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
String tokenValue = jwtAuthenticationToken.getToken().getTokenValue();
template.header(HttpHeaders.AUTHORIZATION,
String.format("%s %s", OAuth2AccessToken.TokenType.BEARER.getValue(), tokenValue));
}
}
}
二、开启服务降级熔断
此时就是不同线程,线程之间是相互隔离的,所以子线程无法获取主线程的内容,此时可以使用RequestContextHolder ,RequestContextHolder 维护了两个容器,一个是不能跨线程的ThreadLocal,一个是实现了InheritableThreadLocal的NamedInheritableThreadLocal。InheritableThreadLocal是可以把父线程的数据传递到子线程的,基于这个原理RequestContextHolder把调用方的请求信息带进了子线程,借助于这个原理就能实现令牌中继了。
@Configuration
public class MyOAuthRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (Objects.nonNull(requestAttributes)) {
String authorizationHeader = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION);
template.header(HttpHeaders.AUTHORIZATION,authorizationHeader);
}
}
}