Zuul网关丢失鉴权信息问题

公司有个项目使用spring cloud全家桶。由zuul做入口,路由到各个业务系统。其中路由完成登陆鉴权。后来有一个业务系统需要用到这些登陆信息,所以希望路由这端将登陆的信息通过消息头的方式带到业务系统。 想想实现很简单,定义一个feign的拦截器,把登陆信息填到header中。不过中间遇到个小问题。统一的路由中将信息保持到了一个ThreadLocal中,拦截器会从这个ThreadLocal中取出数据。问题就出在这里,取到的数据是空的。其他地方也在使用这个ThreadLocal中的数据,并未发现问题,未读这个feign拦截器却不行。最奇怪的本地测试可以,跑测试环境就不行。 再三排查,也查了不少资料,最后看到有博客上提到差不多的问题,原因是Hystrix的策略导致的。Hystrix使用不同的线程完成工作,也就是说Hystrix的线程与Request不再同一个线程中。这样,基于ThreadLocal的一些功能都无法正常使用。后来再stackoverflow上找到了解决办法,连接。框架的开发者也知道这问题,所以提供了一套完整的解决方案。HystrixRequestContext和HystrixRequestVariableDefault就是用来解决这个跨线程时共享数据问题的。简单的说,放入HystrixRequestVariableDefault中的数据,不管是在Requst线程中,还是在Hystrix的线程中,都可以访问到。不过HystrixRequestVariableDefault必须在每一个请求开始的时候进行初始化,同时在请求结束收回收里面的数据。 方案有了,上代码,先实现一个mvc的拦截器,将ThreadLocal中的数据转到HystrixRequestVariableDefault中: public class HystrixContext { public static final String LOGIN_INFO_HEADER_KEY = "his-login-info"; public static final String TOKEN_HEADER_KEY = "token"; public static final HystrixRequestVariableDefault<LoginInfo> LOGIN_INFO = new HystrixRequestVariableDefault<>(); } try { if (!HystrixRequestContext.isCurrentThreadInitialized()) { HystrixRequestContext.initializeContext(); } LoginInfo loginInfo = convertLoginInf(request.getHeader(HystrixContext.LOGIN_INFO_HEADER_KEY)); LoginInfoUtil.setInfo(loginInfo); String token = request.getHeader(HystrixContext.TOKEN_HEADER_KEY); LoginInfoUtil.setToken(token); HystrixContext.LOGIN_INFO.set(LoginInfoUtil.getLoginInfo()); } catch (Exception e) { LOG.error("从头信息中获取数据失败", e); } 然后给feign加一个拦截器: try { if (!HystrixRequestContext.isCurrentThreadInitialized()) { HystrixRequestContext.initializeContext(); } LoginInfo loginInfo = HystrixContext.LOGIN_INFO.get(); if (loginInfo !...

June 28, 2019

spring cloud feign 拦截器配置

问题 公司大规模推全链路,要求所有应用都接入。手头一个新的项目,采用spring cloud框架。通过 feign 调用rest接口。但是支持提供的支持 HttpClient4 的方式无法对 feign 起作用。 思路 想着 feign 也是采用 HttpClient 的。按理对 HttpClient4 起作用的也应该同样有效。然后去看了下架构组全链路配置代码,原来他们是在初始化 HttpCLient 实例时候,加入了拦截器。通过拦截器,在 header 中加入链路信息。但是这个方式似乎对 feign 没啥作用。那我就自己实现添加拦截器呗。 查了下 feign 添加拦截器,网络上提供的方式都是通过自定义 FeignConfiguration 的方式来实现。方法如下: 新建 FeignConfiguration类 public class FeignConfiguration { @Bean public FeignBasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new FeignBasicAuthRequestInterceptor(); } } 然后在配置客户端调用时候加入配置中 @FeignClient(value = "xxx-service", path = "/xxx", configuration = FeignConfiguration.class) 只是这种方法无法做到全局。找了好久,看了文档,都没有办法配置全局。 无奈,去翻源码吧。找到了 FeignAutoConfiguration 类。看到了初始化配置。 protected static class HttpClientFeignConfiguration { @Autowired( required = false ) private HttpClient httpClient; protected HttpClientFeignConfiguration() { } @Bean @ConditionalOnMissingBean({Client....

January 25, 2018