weblog/doc/9、用户服务搭建与开发/9.6 Feign 请求拦截器:实现 userId 服务间透传.md
2025-02-17 11:57:55 +08:00

5.5 KiB
Raw Blame History

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 整个链路就打通了。

本小节源码下载

https://t.zsxq.com/MVY2u