实战!openFeign如何实现全链路JWT令牌信息不丢失?
大家好,我是不才陈某~
这是《Spring Cloud 进阶》第20篇文章,往期文章如下:
- 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强?
- openFeign夺命连环9问,这谁受得了?
- 阿里面试这样问:Nacos、Apollo、Config配置中心如何选型?这10个维度告诉你!
- 阿里面试败北:5种微服务注册中心如何选型?这几个维度告诉你!
- 阿里限流神器Sentinel夺命连环 17 问?
- 对比7种分布式事务方案,还是偏爱阿里开源的Seata,真香!(原理+实战)
- Spring Cloud Gateway夺命连环10问?
- Spring Cloud Gateway 整合阿里 Sentinel网关限流实战!
- 分布式链路追踪之Spring Cloud Sleuth夺命连环9问?
- 链路追踪自从用了SkyWalking,睡的真香!
- 3本书了,7万+字,10篇文章,《Spring Cloud 进阶》基础版 PDF
- 妹子始终没搞懂OAuth2.0,今天整合Spring Cloud Security 一次说明白!
- OAuth2.0实战!使用JWT令牌认证!
- OAuth2.0实战!玩转认证、资源服务异常自定义这些骚操作!
- 实战干货!Spring Cloud Gateway 整合 OAuth2.0 实现分布式统一认证授权!
- 字节面试这样问:跨库多表存在大量数据依赖问题有哪些解决方案?
- 实战!退出登录时如何借助外力使JWT令牌失效?
- 实战!Spring Cloud Gateway集成 RBAC 权限模型实现动态权限控制!
- 实战!阿里神器 Seata 实现 TCC模式 解决分布式事务,真香!
今天这篇文章介绍一下JWT令牌如何在微服务链路中保证信息不丢失?官方称为令牌中继。
什么是令牌中继?
令牌中继通俗的讲则是让令牌在微服务链路调用中传递下去,保证各个微服务能够获取令牌中的用户信息。
以下订单的例子来说,如下图:
下单流程
客户端携带令牌请求网关,网关鉴权成功后会将令牌中的用户信息解析出来放在请求头中下发给订单服务,同样的,订单服务需要将用户信息传递给账户服务获取该用户的账户信息。
那么问题来了?如何保证网关服务->订单服务->账户服务这条链路中的用户信息传递下去是个痛点
解决方案
令牌在openFeign调用过程中是不能自动中继的,因此必须手动的将令牌信息传递下去。
注意:openFeign在开启熔断降级后内部调用开启了子线程,因此传统的方案直接在RequestInterceptor中设置是不可行的。
那么如何保证子线程也能获取请求头中的用户信息呢?
答案是:RequestContextHolder这个神器。
RequestContextHolder内部通过InheritableThreadLocal实现子线程共享信息。
在FeignCircuitBreakerInvocationHandler这个类中也是有如下一行代码:
RequestContextHolder.setRequestAttributes(requestAttributes);
正是使用RequestContextHolder将request的信息保存在其中,因此实现令牌中继只需要读取RequestContextHolder的信息即可。
详细代码如下:
/**
* @author 公众号:码猿技术专栏
* 用于实现令牌信息中继
*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
//从RequestContextHolder中获取HttpServletRequest
HttpServletRequest httpServletRequest = RequestContextUtils.getRequest();
//获取RequestContextHolder中的信息
Map<String, String> headers = getHeaders(httpServletRequest);
//放入feign的RequestTemplate中
for (Map.Entry<String, String> entry : headers.entrySet()) {
template.header(entry.getKey(), entry.getValue());
}
}
/**
* 获取原请求头
*/
private Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
}
源码目录如下图:
“源码已经上传GitHub,关注公众号:码猿技术专栏,回复关键词:9529 获取!”
文章转自公众号:码猿技术专栏