SpringCloudGateway网关处拦截并修改请求

news2024/11/24 15:07:03

SpringCloudGateway网关处拦截并修改请求

img

需求背景

老系统没有引入Token的概念,之前的租户Id拼接在请求上,有的是以Get,Param传参形式;有的是以Post,Body传参的。需要在网关层拦截请求并进行请求修改后转发到对应服务。

举个例子:

Get请求:

/user/getInfo?userId=1 经过网关处理后变为 /user/getInfo?userId=1&&tenantId=2333

Post请求:

/user/getInfo Body携带参数为:

{
  userId: "1"
}

经过网关处理后变为

{
  userId: "1",
  tenantId: "2333"
}

解决办法

  1. 全局过滤器配置: 通过@Bean注解配置一个全局过滤器,用于在请求被转发到微服务前进行处理。
  2. 处理GET请求: 如果是GET请求,直接修改URL并返回,不对请求体进行修改。
  3. 处理非GET请求: 对非GET请求,使用装饰者模式创建ModifyRequestBodyServerHttpRequestDecorator对象,对请求体进行修改。
  4. 去掉Content-Length头: 在修改请求体的同时,通过mutate()方法去掉请求头中的Content-Length
  5. 修改请求体的装饰者类: 定义了一个内部类ModifyRequestBodyServerHttpRequestDecorator,继承自ServerHttpRequestDecorator,用于实现请求体的修改。

代码示例:

// 导入必要的类和包
package com.***.gateway.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

@Configuration
@Slf4j
public class GatewayConfig {

    // 配置全局过滤器
    @Bean
    public GlobalFilter customGlobalFilter() {
        return (exchange, chain) -> {
            // 获取原始请求对象
            ServerHttpRequest request = exchange.getRequest();

            // 构建URI组件构建器,用于修改请求URL
            UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(request.getURI());

            // 初始化租户ID
            String tenantId = "";

            // 检查请求头中是否包含 "TenantId",如果有则获取其值
            if (request.getHeaders().containsKey("TenantId")) {
                tenantId = request.getHeaders().get("TenantId").get(0);
                uriBuilder.queryParam("tenantId", tenantId);
            }

            // 如果请求是GET请求,则直接返回
            if (request.getMethodValue().equals("GET")) {
                log.info("请求是Get请求,url is {}", uriBuilder.build().toUri());
                ServerHttpRequest modifiedRequest = request.mutate().uri(uriBuilder.build().toUri()).build();

                // 创建新的ServerWebExchange,该对象包含修改后的请求
                ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();

                // 继续执行过滤器链
                return chain.filter(modifiedExchange);
            }

            // 使用装饰者模式修改请求体
            ServerHttpRequest modifiedRequest = new ModifyRequestBodyServerHttpRequestDecorator(request, tenantId, exchange.getResponse().bufferFactory());

            // 去掉Content-Length请求头
            modifiedRequest = modifiedRequest.mutate().header("Content-Length", (String) null).build();

            // 创建新的ServerWebExchange,该对象包含修改后的请求
            ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();

            // 继续执行过滤器链
            return chain.filter(modifiedExchange);
        };
    }

    // 定义修改请求体的装饰者类
    private static class ModifyRequestBodyServerHttpRequestDecorator extends ServerHttpRequestDecorator {

        private final String tenantId;
        private final DataBufferFactory bufferFactory;
        private final ObjectMapper objectMapper = new ObjectMapper();
 

        // 构造方法,传入原始请求、tenantId和数据缓冲工厂
        ModifyRequestBodyServerHttpRequestDecorator(ServerHttpRequest delegate, String tenantId, DataBufferFactory bufferFactory) {
            super(delegate);
            this.tenantId = tenantId;
            this.bufferFactory = bufferFactory;
        }

        // 重写获取请求体的方法,对请求体进行修改
        @NotNull
        @Override
        public Flux<DataBuffer> getBody() {
            return super.getBody().map(dataBuffer -> {
                // 读取原始请求体数据
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                String body = new String(bytes, StandardCharsets.UTF_8);

                // 修改请求体内容
                String newBody = modifyJsonBody(body);

                // 创建新的 DataBuffer
                byte[] newData = newBody.getBytes(StandardCharsets.UTF_8);
                return bufferFactory.wrap(newData);
            });
        }

        // 对 JSON 请求体进行修改,添加 tenantId 字段
        private String modifyJsonBody(String originalBody) {
            try {
                JsonNode jsonNode = objectMapper.readTree(originalBody);
                ((ObjectNode) jsonNode).put("tenantId", tenantId);
                return objectMapper.writeValueAsString(jsonNode);
            } catch (IOException e) {
                log.error("Error modifying JSON body", e);
                return originalBody;
            }
        }
    }
}

解决路径文章参考

http://t.csdnimg.cn/9kos5

http://t.csdnimg.cn/Aklwh

关于装饰者模式

装饰者模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装类中来为原始对象添加新的行为。这种模式能够在不修改原始对象的情况下,动态地扩展其功能。在上段代码里,主要使用装饰者模式去修改Body 的传参。

主要角色:

  1. Component(组件): 定义一个抽象接口或抽象类,声明对象的一些基本操作。
  2. ConcreteComponent(具体组件): 实现了Component接口,是被装饰的具体对象,也是我们最终要添加新行为的对象。
  3. Decorator(装饰者抽象类): 继承了Component,并持有一个Component对象的引用,同时实现了Component定义的接口。它可以通过该引用调用Component的操作,同时可以添加、扩展或修改Component的行为。
  4. ConcreteDecorator(具体装饰者): 扩展Decorator,具体实现新行为的类。

装饰者模式的工作流程:

  1. 客户端通过Component接口与ConcreteComponent对象进行交互。
  2. ConcreteComponent对象处理客户端的请求。
  3. 客户端可以通过Decorator接口与ConcreteDecorator对象进行交互,Decorator持有ConcreteComponent的引用。
  4. ConcreteDecorator在调用ConcreteComponent的操作前后,可以添加、扩展或修改行为。

给普通咖啡加点糖和牛奶

代码示例:



public class DecoratorPatternExample {

    // Component(组件)
    interface Coffee {
        String getDescription();
        double cost();
    }

    // ConcreteComponent(具体组件)
    static class SimpleCoffee implements Coffee {
        @Override
        public String getDescription() {
            return "Simple Coffee";
        }

        @Override
        public double cost() {
            return 1.0;
        }
    }

    // Decorator(装饰者抽象类)
    abstract static class CoffeeDecorator implements Coffee {
        protected Coffee decoratedCoffee;

        public CoffeeDecorator(Coffee coffee) {
            this.decoratedCoffee = coffee;
        }

        @Override
        public String getDescription() {
            return decoratedCoffee.getDescription();
        }

        @Override
        public double cost() {
            return decoratedCoffee.cost();
        }
    }

    // ConcreteDecorator(具体装饰者)
    static class MilkDecorator extends CoffeeDecorator {
        public MilkDecorator(Coffee coffee) {
            super(coffee);
        }

        @Override
        public String getDescription() {
            return super.getDescription() + ", with Milk";
        }

        @Override
        public double cost() {
            return super.cost() + 0.5;
        }
    }

    // ConcreteDecorator(具体装饰者)
    static class SugarDecorator extends CoffeeDecorator {
        public SugarDecorator(Coffee coffee) {
            super(coffee);
        }

        @Override
        public String getDescription() {
            return super.getDescription() + ", with Sugar";
        }

        @Override
        public double cost() {
            return super.cost() + 0.2;
        }
    }
    public static void main(String[] args) {
        // 创建一个简单的咖啡
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println("Cost: " + simpleCoffee.cost() + ", Description: " + simpleCoffee.getDescription());

        // 使用装饰者模式添加牛奶和糖
        Coffee milkSugarCoffee = new MilkDecorator(new SugarDecorator(simpleCoffee));
        System.out.println("Cost: " + milkSugarCoffee.cost() + ", Description: " + milkSugarCoffee.getDescription());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1327635.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件工程快速复习(期末急救)

每个同学要假想自己是一个项目经理&#xff0c;去完成一个软件项目&#xff0c;比如医院管理系统&#xff0c;自动设备控制系统等&#xff0c;以面向结构的软件工程方法&#xff0c;说出完成项目的步骤&#xff0c;涉及到的具体技术。初步了解面向对象的方法的与面向结构的方法…

Linux网络编程——字节序

一、概念 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。 二、分类 字节序有两者常见序&#xff1a; 1. Big-Endian&#xff08;大端&#xff09;&#xff1a;高位字节排放在内存的低地址端&#xff0c;低位字节排放在内存的高地址端。如&#x…

量化交易学习笔记:XGBoost 在量化选股中的应用

一、引言 本篇文章通过借鉴传统机器学习算法——XGBoost——对相同的量价因子进行实验&#xff0c;方便与深度学习模型进行对比实践。 二、算法介绍 XGBoost 是在 Gradient Boosting&#xff08;梯度提升&#xff09;框架下实现的机器学习算法&#xff0c;全称为“极限梯度提…

图神经网络并在 TensorFlow 中实现

asokraju.medium.com 一、说明 本文将引导您了解图神经网络 (GNN) 并使用 TensorFlow 实现该网络。在后续的 文章中&#xff0c;我们讨论 GNN 的不同变体及其实现。这是一个分步计划&#xff1a; 图神经网络 (GNN) 的使用&#xff1a;我们首先讨论 GNN 是什么、它们如何工作以及…

论文阅读<MULTISCALE DOMAIN ADAPTIVE YOLO FOR CROSS-DOMAIN OBJECT DETECTION>

论文链接&#xff1a;https://arxiv.org/pdf/2106.01483v2.pdfhttps://arxiv.org/pdf/2106.01483v2.pdf 代码链接&#xff1a;GitHub - Mazin-Hnewa/MS-DAYOLO: Multiscale Domain Adaptive YOLO for Cross-Domain Object DetectionMultiscale Domain Adaptive YOLO for Cross…

[JS设计模式]Command Pattern

文章目录 举例说明优点缺点完整代码 With the Command Pattern, we can decouple objects that execute a certain task from the object that calls the method. 使用命令模式&#xff0c;我们可以将执行特定任务的对象与调用该方法的对象解耦。 怎么理解 执行特定任务的对…

【SpringBoot】之Security进阶使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之Security系列》。&#x1f3af…

解决 Linux git push 贡献者不同(没有出现绿点)的问题

第一步&#xff0c;通过下面的指令&#xff0c;修改 linux git 的配置文件&#xff1a; vi ~/.gitconfig会进入下图界面&#xff1a; 进入本地&#xff08;Windows&#xff09;中 git 的设置界面 复制 名称 和 Email 到 gitconfig 里&#xff0c;不要在末尾加 &#xff08;空…

直排轮滑教程4

蹬地 1&#xff0c;前面练习了蹬地的结构&#xff0c;知道蹬地方向&#xff0c;如何用力。下面来练习具体的蹬地的方法&#xff0c;轮滑蹬地有自己特点。 2&#xff0c;技术方法和特点&#xff1a;蹬地速度快&#xff0c;蹬地有弹性。似跳非跳蹬。 3&#xff0c;四轮着地。轮…

使用PE信息查看工具和Beyond Compare文件比较工具排查dll文件版本不对的问题

目录 1、问题说明 2、修改了代码&#xff0c;但安装版本还是有问题 3、使用PE信息查看工具查看音视频库文件&#xff08;二进制&#xff09;的时间戳 4、使用Beyond Compare比较两个库文件的差异 5、找到原因 6、最后 C软件异常排查从入门到精通系列教程&#xff08;专栏…

NFS原理详解

一、NFS介绍 1&#xff09;什么是NFS 它的主要功能是通过网络让不同的机器系统之间可以彼此共享文件和目录。 NFS服务器可以允许NFS客户端将远端NFS服务器端的共享目录挂载到本地的NFS客户端中。 在本地的NFS客户端的机器看来&#xff0c;NFS服务器端共享的目录就好像自己的磁…

【蓝桥杯】树的重心

树的重心 图的dfs模板 int dfs(int u) {st[u]true;for(int ih[u];i!-1;ine[i]){int je[i];if(!st[j]){dfs(j);}} }树是这样的。 邻接表&#xff1a; 1: 4->7->2->-1 2: 5->8->1->-1 3: 9->4->-1 4: 6->3->1->-1 5: 2->-1 6: 4->-1 7…

计算机网络 运输层下 | TCP概述 可靠传输 流量控制 拥塞控制 连接管理

文章目录 3 运输层主要协议 TCP 概述3.1 TCP概述 特点3.2 TCP连接RSVP资源预留协议 4 TCP可靠传输4.1 可靠传输工作原理4.1.1 停止等待协议4.1.2 连续ARQ协议 4.2 TCP可靠通信的具体实现4.2.1 以字节为单位的滑动窗口4.2.2 超时重传时间的选择4.2.3 选择确认SACK 5 TCP的流量控…

Python---socket之send和recv原理剖析

1. 认识TCP socket的发送和接收缓冲区 当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区&#xff0c;这个发送和接收缓冲区指的就是内存中的一片空间。 2. send原理剖析 send是不是直接把数据发给服务端? 不是&#xff0c;要想发数据&#xff0c;必须得…

GEE-Sentinel-2月度时间序列数据合成并导出

系列文章目录 第一章&#xff1a;时间序列数据合成 文章目录 系列文章目录前言时间序列数据合成总结 前言 利用每个月可获取植被指数数据取均值&#xff0c;合成月度平均植被指数&#xff0c;然后将12个月中的数据合成一个12波段的时间数据合成数据。 时间序列数据合成 代码…

嵌入式中断理解

一、概念 中断&#xff1a; 在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行。 中断优先级&#x…

YACS(上海计算机学会竞赛平台)一星级题集——水仙花指数

题目描述 定义一个正整数的十进制表示中各位数字的立方和为它的水仙花指数&#xff0c;给定一个整数 n&#xff0c;请计算它的水仙花指数。 例如 n1234 时&#xff0c;水仙花指数为 输入格式 单个整数&#xff1a;表示 n 输出格式 单个整数&#xff1a;表示 n 的水仙花指…

IPC之九:使用UNIX Domain Socket进行进程间通信的实例

socket 编程是一种用于网络通信的编程方式&#xff0c;在 socket 的协议族中除了常用的 AF_INET、AF_RAW、AF_NETLINK等以外&#xff0c;还有一个专门用于 IPC 的协议族 AF_UNIX&#xff0c;IPC 是 Linux 编程中一个重要的概念&#xff0c;常用的 IPC 方式有管道、消息队列、共…

深入探讨开源对话系统:IntelliQ的世界

在人工智能的快速发展时代&#xff0c;开源项目成为了推动技术革新的重要力量。最近&#xff0c;我注意到了一个特别有趣的项目——IntelliQ。这个项目旨在利用大型语言模型&#xff08;LLM&#xff09;构建一个多轮问答系统&#xff0c;不仅具备强大的意图识别和词槽填充&…

【Unity】【WebRTC】如何用Unity而不是浏览器接收远程画面

【背景】 之前几篇我们讨论了如何设置信令服务器&#xff0c;如何发送画面给远端以及如何用浏览器查看同步画面&#xff0c;今天来讨论如何实现Unity内部接收画面。 看本篇之前请先看过之前将web服务器设置和基本远程画面功能的几篇博文。&#xff08;同专栏下查看&#xff09…