微服务参数透传实现

news/2024/7/24 10:06:42 标签: 微服务, 架构, SpringBoot

说明:在微服务架构中,用户身份经网关验证后,我们可以将用户信息,如ID加入到请求头上。后面的微服务中,可以设置一个拦截器,拦截请求,获取请求头上的用户ID,加入到ThreadLocal中。

最重要的是,在当前微服务发起请求,调用其他微服务时,需要将发出去的请求再进行拦截,把本地线程池中的用户ID再次放入到请求头中,这样就实现了用户信息在微服务中的流转,称为参数透传。本文介绍,上述参数透传的实现。

在这里插入图片描述

微服务系统中,一般都会有一个共同模块,是所有微服务共同引用的模块,比如模块名为Base、Util、Feign-API,我们可以把参数透传的拦截器写在这个模块里面,这样其他微服务就不用再写拦截器的配置类了。

比如,我当前的这个系统,Feign-API模块是所有微服务共同引用的。里面写了一些共同的DTO对象,以及Feign-Client类对象;

在这里插入图片描述

实现

第一步:创建ThreadLocal类

在FeignAPI模块中,创建一个本地线程池对象,用于存储当前用户的ID;

/**
 * 本地线程池
 */
public class TokenThreadLocal {
    
    private static final ThreadLocal<Long> THREAD_LOCAL= new ThreadLocal<>();

    /**
     * 设置
     * @param tokenId
     */
    public static void set(Long tokenId){
        THREAD_LOCAL.set(tokenId);
    }

    /**
     * 获取
     * @return
     */
    public static Long get(){
        return THREAD_LOCAL.get();
    }

    /**
     * 移除
     */
    public static void remove(){
        THREAD_LOCAL.remove();
    }
}

第二步:创建MVC拦截器

在FeignAPI模块中,创建MVC拦截器(HandlerInterceptor),拦截路径为所有路径;

package com.hzy.interceptor;

import com.hzy.config.TokenThreadLocal;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 接收请求拦截器
 */
public class AuthorizationInterceptor implements HandlerInterceptor {

    /**
     * 收到请求会执行的方法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        // 获取请求头中的用户ID
        String id = request.getHeader("authorization");

        // 判断用户ID是否为空
        if (id != null && id != ""){
            // 将字符串转换为Long类型
            long l = Long.parseLong(id);
            
            // 将用户ID存入ThreadLocal中
            TokenThreadLocal.set(l);
        }else {
            responseHandler(response);
            return false;
        }

        // 放行
        return true;
    }

    /**
     * 请求返回客户端之前会执行的方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 请求到客户端之后会执行的方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        TokenThreadLocal.remove();
    }

    /**
     * 用户尚未登录的返回信息
     * @param response
     * @return
     * @throws IOException
     */
    private boolean responseHandler(HttpServletResponse response) throws IOException {
        // 设置响应状态码 401 如果用户未登录 状态码都返回401
        response.setStatus(HttpStatus.UNAUTHORIZED.value());

        // 响应的数据类型和编码格式
        response.setContentType("application/json;charset=UTF-8");

        // 提示信息
        response.getWriter().write("用户未登录");

        return false;
    }
}

拦截器配置类,设置为所有路径请求都拦截;

import com.hzy.interceptor.AuthorizationInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 接收请求拦截器配置类
 */
public class MVCInterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthorizationInterceptor())
                .addPathPatterns("/**");
    }
}

第三步:创建Feign拦截器

创建Feign的拦截器(RequestInterceptor),该拦截器用于拦截当前微服务发出去的请求,代码内容写把当前本地线程池的用户ID再次放入到请求头中,传给下游微服务解析;

import com.hzy.config.TokenThreadLocal;
import feign.RequestInterceptor;
import feign.RequestTemplate;

/**
 * 发送请求拦截器
 */
public class AuthorizationRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 从本地线程池中获取用户ID,放入请求头中
        requestTemplate.header("authorization",TokenThreadLocal.get().toString());
    }
}

测试

测试订单服务,生成订单中,会调用库存服务,查询库存信息。在MVC、Feign拦截器、调用库存服务行分别打断点,发送请求,查看执行过程;

在这里插入图片描述

请求通过网关,先被订单服务的MCV拦截器拦截到,可以看到当前用户的ID是2,说明网关的默认过滤器起作用了,放行……

在这里插入图片描述

然后,断点来到订单服务的,调用库存服务这里,放行,查看发出去的请求会不会被拦截住;

在这里插入图片描述

断点被Feign拦截器(RequestInterceptor)拦住,将参数放入到请求头中;

在这里插入图片描述

这样,就实现了用户参数在微服务系统中的流转。

总结

使用一前一后两个拦截器,对微服务接受请求前,发送请求前分别进行拦截,实现参数透传。但是需要注意以下两点:

  • 两个拦截器所依赖的是本地线程池,所以如果微服务发出的是异步消息(如MQ),就行不通(参考:http://t.csdn.cn/wWsb7);

  • 拦截器是写在共同模块中的,所以参数得以流转的前提是当前微服务引用了共同模块


http://www.niftyadmin.cn/n/4949729.html

相关文章

剑指Offer48.最长不含重复字符的子字符串 C++

1、题目描述 请从字符串中找出一个最长的不包含重复字符的子字符串&#xff0c;计算该最长子字符串的长度。 示例 1: 输入: “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: “bbbbb” 输出: 1 解释: 因为无重复字…

罗勇军 →《算法竞赛·快冲300题》每日一题:“质因子数量” ← 快速幂、素数筛

【题目来源】http://oj.ecustacm.cn/problem.php?id1780http://oj.ecustacm.cn/viewnews.php?id1023【题目描述】 给出n个数字&#xff0c;你可以任意选择一些数字相乘&#xff0c;相乘之后得到新数字x。 其中&#xff0c;x的分数等于x不同质因子的数量。 请你计算所有选择数…

android 上拉固定高度显示,下拉关闭View BottomSheetDialog

具体代码 复制使用 1,新建一个BottomBaseStationDialogLayout.java类继承BottomSheetDialogFragment public class BottomBaseStationDialogLayout extends BottomSheetDialogFragment { public static BottomBaseStationDialogLayout getInstance() {return new BottomBase…

初学者SpringBoot+Vue打通前后端详细步骤(从零开始)

目录 前言介绍 一、后端SpringBoot项目创建 &#xff08;一&#xff09;springboot后端实现增删改查 二、前端Vue项目的创建 &#xff08;一&#xff09;下载必要的环境&#xff08;有则跳过&#xff09; &#xff08;二&#xff09;创建vue项目并使用Element-ui 三、前…

使用 Spring Security LDAP 实现身份验证

如果你曾经在生产级应用程序中实现过登录功能&#xff0c;你一定听说过 LDAP 身份验证。 LDAP 可用于任何类型的分层目录信息&#xff0c;LDAP 最流行的用途是存储组织数据。 举个例子&#xff0c;比如说你们公司一个典型的组织结构&#xff0c;那么一般都有董事、经理、监事…

打通应用“壁垒”,数据分类分级结果与安全策略自动匹配

《网络安全法》、《数据安全法》等法律法规&#xff0c;以及各行业各领域与数据安全相关的标准规范&#xff0c;几乎都涉及对数据进行分类分级保护的要求。数据安全始于分类分级&#xff0c;已成为毫无疑问的行业共识。 但现实中不少用户却止步在分类分级工作&#xff0c;“如…

CSS基础 知识点总结

一.CSS简介 1.1 CSS简介 ① CSS指的是层叠样式表&#xff0c;用来控制网页外观的一门技术 ② CSS发展至今&#xff0c;经历过CSS1.0 CSS2.0 CSS2.1 CSS3.0这几个版本&#xff0c;CSS3.0是CSS最新版本 1.2 CSS引入方式 ① 在一个页面引入CSS&#xff0c;共有三种方式 外部…

16.5.4 【Linux】SELinux 政策内的规则管理

SELinux 各个规则的布林值查询 getsebool 如果想要查询系统上面全部规则的启动与否 &#xff08;on/off&#xff0c;亦即布林值&#xff09;&#xff0c;很简单的通过 sestatus-b 或 getsebool -a 均可&#xff01; SELinux 各个规则规范的主体程序能够读取的文件 SELinux typ…