feign远程调用丢失请求头源码分析与解决

Feign远程调用丢失Cookie问题
探讨Feign远程调用导致Cookie丢失的原因及解决办法,包括如何自定义RequestInterceptor来同步请求头。

前言

我们在写服务端项目的时候,总会限制对某些资源的访问,最常见的就是要求用户先登录才能访问资源,当用户登录后就会将此次会话信息保存进session,同时返回给浏览器指定的cookie键值,下次浏览器再次访问,请求头中就会携带这个cookie,我们也以次来识别用户的登录状态,做出正确响应。

问题

有时候,我们先行登录,然后访问服务A的某个方法,请求头中携带cookie,标识我们已经登录。但若是我们访问的目标方法在执行过程中使用feign进行原程调用服务B(假设不存在跨域),而服务B也要先判断登录状态,我们可能发现服务B会调用失败,或者说拿不到数据,理由是服务B认为我们并未登录。而这时,如果我们直接从浏览器访问服务B的这个方法却能得到一个成功的响应。

也就是说:

浏览器—>服务A成功; 服务A–>服务B失败; 浏览器–>服务B失败
结合上面所说,服务AB都会先判断用户登录状态,浏览器直接访问AB时都会带上登录成功后保存的cookie,而服务A通过Feign远程调用B,却被认为未登录,显然,这部分请求头数据丢失。

feign源码分析

我们来看下feign远程调用是如何执行的,我们在feign远程调用之处打上断点
在这里插入图片描述

  1. step into进入方法执行,会发现是一个代理对象的invoke方法在执行,首先判断是方法名,
  • 如果是 toString(),hashCode(),equals()这几个方法,那就是本地直接完成了。
  • 如果是真正的远程调用,就会最后进入最后一行。
    在这里插入图片描述
  1. 上一步之后再次step into,发现还是一个invoke方法,方法内,首先根据请求参数创建一个RequestTemplate,核心部分是 while(true) 里面的 executeAndDecode(),while 其实是加了一层重试机制,这里不多说。
    在这里插入图片描述
  2. 进入executeAndDecode方法,我们看到 targetRequest() 构造出了一个request对象,而最终的response就是这个request请求的执行结果。
    在这里插入图片描述
    同时我们能够看到这个request对象的请求头中是空的,当然也就不存在cookie,也就无法识别我们是否登录。
    在这里插入图片描述

小结

  • 使用feign进行远程调用时,首先判断目标方法类型,如果是 toString(),hashCode(),equals()这几个方法,那就是本地直接完成了;
  • 如果是真正的远程调用,执行的是 executeAndDecode 方法,在这个方法体内,会通过 targetRequest 方法创建出一个新的 request 对象,这个新的request会按照我们指定的参数和路径去发送请求,并获得响应结果。
  • 这个新的request对象的请求头为空(所以会丢失原来的请求头)

解决

问题在于feign自己创建出resttemplate,再用它构建一个新的request对象去发送请求,而这个新的request不包含任何请求头信息。我们应该在它创造出这个request之后,在它真正发送请求之前,把原始请求头中的数据给它复制过去。

我们来看一下feign最后构建出创建request对象的 targetRequest方法
在这里插入图片描述
我们发现这里面会有调用了一系列 RequestInterceptorapply方法对其进行增强,最后才返回,只不过默认情况下这些拦截器是空的。

因此 ,我们需要需要自己实现一个 RequestInterceptor,在它的apply方法中将原始请求头中的数据同步到feign创建出的新的request中,并且将这个拦截器注入容器中,这样feign在执行目标方法之前会被其拦截,对其先进行增强。

@Component
public class FeignBeforeExecIntercept
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值