浏览器发送请求时携带了cookie信息,A服务拦截器根据cookie和session获得用户信息,但当A服务调用B服务时,由于创建了一个新的请求,这些信息都没有携带,将会导致B服务拦截器认证失败而拒绝访问。
解决方案1:非异步业务逻辑
调用远程服务前后为同一线程时可以使用以下方法,但不适用于使用线程池对业务逻辑异步编排的场景。
首先在config包下创建配置类MyFeignConfig
@Configuration
public class MyFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor() {
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
//1、使用RequestContextHolder拿到刚进来的请求数据
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
// 原请求
HttpServletRequest request = requestAttributes.getRequest();
if (request != null) {
//2、同步请求头的数据(主要是cookie)
//把老请求的cookie值放到新请求上来,进行一个同步
String cookie = request.getHeader("Cookie");
template.header("Cookie", cookie);
}
}
}
};
return requestInterceptor;
}
}
原因浅析:
服务远程调用时,底层会调用requestInterceptor这个Bean,当我们配置了这个配置类后,相当于重写了拦截器定义,将远程调用新创建的请求做一点处理,相当于把原请求的携带的信息取出存入新请求中。其中我们从RequestContextHolder中获取到RequestAttributes里的数据,但是RequestContextHolder的实现依赖于ThreadLocal,因此必须是同一线程才能取出原请求信息。
解决方案2:异步业务逻辑
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//每一个线程都来共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);
// 业务逻辑代码
}, executor);
首先在配置完上述步骤的基础之上,在异步代码段执行之前,先手动获取RequestContextHolder中的请求域;异步代码段中,业务代码之前手动设置请求域即可。