SpringCloud OpenFeign 服务调用传递 token

本文详细探讨了在SpringCloud OpenFeign中如何在服务调用时传递token,通过RequestInterceptor实现请求头的添加。在多线程环境中,由于ThreadLocal特性,子线程无法直接获取主线程的RequestAttributes。通过对inheritableRequestAttributesHolder原理的分析,了解到子线程获取header失败的原因是Tomcat在请求结束后重置了request对象。解决方案包括控制主线程等待子线程完成或使用自定义ThreadLocal保存header。
摘要由CSDN通过智能技术生成

业务场景

通常微服务对于用户认证信息解析有两种方案

  • 在 gateway 就解析用户的 token 然后路由的时候把 userId 等相关信息添加到 header 中传递下去。
  • 在 gateway 直接把 token 传递下去,每个子微服务器自己在过滤器解析 token

现在有一个从 A 服务调用 B 服务接口的内部调用业务场景,无论是哪种方案我们都需要把 header 从 A 服务传递到 B 服务。

RequestInterceptor

OpenFeign 给我们提供了一个请求拦截器 RequestInterceptor ,我们可以实现这个接口重写 apply 方法将当前请求的 header 添加到请求中去,传递给下游服务, RequestContextHolder 可以获得当前线程绑定的 Request 对象

/** Feign 调用的时候传token到下游 */
public class FeignRequestInterceptor implements RequestInterceptor {
  @Override
  public void apply(RequestTemplate template) {
    // 从header获取X-token
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes attr = (ServletRequestAttributes) requestAttributes;
    HttpServletRequest request = attr.getRequest();
    String token = request.getHeader("x-auth-token");//网关传过来的 token
    if (StringUtils.hasText(token)) {
      template.header("X-AUTH-TOKEN", token);
    }
  }
}
复制代码

然后在 @FeignClient 中使用

@FeignClient(
    ...
    configuration = {FeignClientDecoderConfiguration.class, FeignRequestInterceptor.class})
public interface AuthCenterClient {
复制代码

多线程环境下传递 header(一)

上面是单线程的情况,假如我们在当前线程中又开启了子线程去进行 Feign 调用,那么是无法从 RequestContextHolder 获取到 header 的,原因很简单,看下 RequestContextHolder 源码就知道了,它里面是一个 ThreadLocal ,线程都变了,那肯定获取不到主线程请求里面的 requestAttribute 了。

原因已经清楚了,现在想办法去解决它。观察
RequestContextHolder.getRequestAttributes() 方法源码

public static RequestAttributes getRequestAttributes() {
   RequestAttributes a
Spring Cloud OpenFeign是一个基于Netflix Feign的超轻量级API客户端库,它简化了服务发现和调用远程服务的过程。在3.1.9版本中,设置请求头主要是通过`@RequestLine`注解和`RequestInterceptor`来完成的。 1. **添加请求头**:如果你想要向服务添加自定义请求头,例如认证信息,可以在FeignClient接口的方法上使用`@RequestLine`注解,并在其中指定`headers`属性。例如: ```java @FeignClient(name = "your-service", url = "${your.service.url}") public interface YourServiceClient { @RequestMapping(value = "/api", method = RequestMethod.GET) String getData(@RequestLine("Authorization: Bearer {token}") String token); } ``` 这里,`Bearer {token}`会被实际传入的token值替换。 2. **全局请求拦截器**:如果你想对每个请求都添加相同的头部,可以创建一个`RequestInterceptor`实现`org.springframework.web.client.RestTemplate`的`Intercepter`接口,然后注册到全局配置中: ```java @Configuration public class GlobalFeignConfig { @Bean public RequestInterceptor requestHeaderInterceptor() { return new RequestInterceptor() { @Override public void apply(RequestTemplate request) { request.header("X-Custom-Header", "value"); } }; } @Bean @Primary public ClientFactory defaultFeignClientFactory() { return new Feign.Builder() .globalRequestInterceptor(requestHeaderInterceptor()) .build(); } } ``` 这样,每次调用FeignClient都会包含这个自定义的头部。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值