5.5 KiB
title, url, publishedTime
| title | url | publishedTime |
|---|---|---|
| Feign 请求拦截器:实现 userId 服务间透传 - 犬小哈专栏 | https://www.quanxiaoha.com/column/10314.html | null |
在进入本小节之前,先来思考一个问题,如果说,我们想在下游服务中获取当前请求对应的用户 ID , 比如,修改用户信息接口,会调用对象存储微服务,在对象存储微服务中,通过前面封装的上下文组件获取当前用户 ID,能拿的到吗?
车祸现场
让我们来实测一下,编辑 xiaohashu-oss-biz 模块的 pom.xml , 添加上下文组件依赖:
<!-- 上下文组件 -->
<dependency>
<groupId>com.quanxiaoha</groupId>
<artifactId>xiaoha-spring-boot-starter-biz-context</artifactId>
</dependency>
刷新 Maven 依赖,然后,在文件上传接口中打印一行测试日志,如下:
log.info("当前用户 ID: {}", LoginUserContextHolder.getUserId());
重启对象存储服务,请求一波用户信息修改接口,观察对象存储服务的控制台日志,会看到无法获取当前用户 ID, 值为 null , 如下图所示:
为什么获取不到?
原因是网关路由转发到服务时,网关层会设置用户 ID 到请求头中,但是,服务之间调用是通过 Feign 来完成的,是没有经过网关的,下游服务再去从请求头中获取用户 ID,自然是拿不到。
如何解决上面这个问题呢?
可以为
Feign单独配置一个请求拦截器,在调用其他服务时,将当前用户 ID 添加到请求头中,保证下游服务也能够通过上下文组件拿到用户 ID。
配置 Feign 请求拦截器
我们将这个功能,一并放到上下文组件中。首先,编辑 xiaoha-spring-boot-starter-biz-context 上下文组件的 pom.xml, 添加 feign 核心依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
接着,在上下文组件中,创建一个 /interceptor 包,用于放置拦截器,并新建 FeignRequestInterceptor 请求拦截器:
代码如下:
package com.quanxiaoha.framework.biz.context.interceptor;
import com.quanxiaoha.framework.biz.context.holder.LoginUserContextHolder;
import com.quanxiaoha.framework.common.constant.GlobalConstants;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
/**
* @author: 犬小哈
* @date: 2024/4/14 17:48
* @version: v1.0.0
* @description: Feign 请求拦截器
**/
@Slf4j
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 获取当前上下文中的用户 ID
Long userId = LoginUserContextHolder.getUserId();
// 若不为空,则添加到请求头中
if (Objects.nonNull(userId)) {
requestTemplate.header(GlobalConstants.USER_ID, String.valueOf(userId));
log.info("########## feign 请求设置请求头 userId: {}", userId);
}
}
}
- 自定义
Feign请求拦截器需继承自RequestInterceptor接口;- 在
apply()方法中,先通过LoginUserContextHolder.getUserId();拿到当前请求对应的用户 ID;- 判断若不为空,则将用户 ID 添加到请求头中,以便下游服务再次获取;
自动化配置
然后,在 /config 包下,新建一个 FeignContextAutoConfiguration 自动化配置类:
代码如下:
package com.quanxiaoha.framework.biz.context.config;
import com.quanxiaoha.framework.biz.context.interceptor.FeignRequestInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
/**
* @author: 犬小哈
* @date: 2024/4/15 13:50
* @version: v1.0.0
* @description: Feign 请求拦截器自动配置
**/
@AutoConfiguration
public class FeignContextAutoConfiguration {
@Bean
public FeignRequestInterceptor feignRequestInterceptor() {
return new FeignRequestInterceptor();
}
}
- 将刚刚自定义的
FeignRequestInterceptor请求拦截器,自动注入到 Spring 容器中。
同时,别忘了在 org.springframework.autoconfigure.AutoConfiguration.imports 文件中,添加上 FeignContextAutoConfiguration 的完整包路径,如下所示:
com.quanxiaoha.framework.biz.context.config.FeignContextAutoConfiguration
重新打包
组件更新完毕后,还需要执行 clean install 命令,将其打包到本地仓库中,以便其他服务能够使用最新版本的组件:
自测一波
重新刷一下 Maven 依赖,并重启用户服务和对象存储服务。再次测试用户信息修改接口,调用成功后,观察对象存储控制台日志,就可以看到,下游服务也成功拿到了当前用户 ID 了,自此,网关 - 上游服务 - 下游服务 透传用户 ID 整个链路就打通了。