SpringCloudGateway网关处拦截并修改请求

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 {// 配置全局过滤器@Beanpublic GlobalFilter customGlobalFilter() {return (exchange, chain) -> {// 获取原始请求对象ServerHttpRequest request = exchange.getRequest();// 构建URI组件构建器,用于修改请求URLUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(request.getURI());// 初始化租户IDString 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@Overridepublic 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);// 创建新的 DataBufferbyte[] 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 {@Overridepublic String getDescription() {return "Simple Coffee";}@Overridepublic double cost() {return 1.0;}}// Decorator(装饰者抽象类)abstract static class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}@Overridepublic String getDescription() {return decoratedCoffee.getDescription();}@Overridepublic double cost() {return decoratedCoffee.cost();}}// ConcreteDecorator(具体装饰者)static class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return super.getDescription() + ", with Milk";}@Overridepublic double cost() {return super.cost() + 0.5;}}// ConcreteDecorator(具体装饰者)static class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return super.getDescription() + ", with Sugar";}@Overridepublic 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.mzph.cn/news/237867.shtml

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

相关文章

工业自动化的通信核心—钡铼技术R10A工业级路由器介绍

随着工业自动化的快速发展&#xff0c;工业通信技术也日新月异。在这个信息时代&#xff0c;工业通信设备的稳定性、可靠性和高效性变得尤为重要。作为工业自动化的核心部件之一&#xff0c;钡铼技术R10A工业级路由器以其出色的性能和卓越的功能在行业内赢得了广泛的赞誉。本文…

docker学习(十二、Redis主从容错迁移)

文章目录 一、容错切换迁移挂一个master节点6381&#xff0c;查看集群信息主节点挂一个&#xff0c;对应从节点切换为主节点&#xff0c;数据获取测试恢复挂的主节点&#xff0c;主从关系变化 二、小思考 docker搭建Redis集群相关知识&#xff1a; docker学习&#xff08;九、分…

C# SQLite基础工具类

目录 1、安装System.Data.SQLite工具包 2、创建数据库 3、数据库的连接与断开 4、执行一条SQL语句 5、批量执行sql语句 6、返回首行首列值 7、执行sql语句返回datatable 1、安装System.Data.SQLite工具包 2、创建数据库 /// <summary> /// 数据库路径 …

Github2023-12-22开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-22统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4TypeScript项目2非开发语言项目2C项目1C项目1HTML项目1Dart项目1 Tailwind CSS&#xff1a;快速U…

kibana-7.15.2 一分钟下载、安装、部署 linux

文章目录 一、下载安装部署 1. 下载2. 解压3. 修改配置 二、kibana 启动 2.1. 创建kibana 用户2.2. 赋予权限2.3. 切换用户2.4. kibana启动2.5. 监控服务2.6. 监控服务2.7. kibana停止2.8. 效果图 三、kibana 启动2 3.1. 浏览器访问3.2. 效果图 一、下载安装部署 https:…

HTML美化网页

使用CSS3美化的原因 用css美化页面文本,使页面漂亮、美观、吸引用户 可以更好的突出页面的主题内容,使用户第一眼可以看到页面主要内容 具有良好的用户体验 <span>标签 作用 能让某几个文字或者某个词语凸显出来 有效的传递页面信息用css美化页面文本&#xff0c;使页面漂…

面试题:JVM 对锁都进行了哪些优化?

文章目录 锁优化自旋锁和自适应自旋锁消除锁粗化逃逸分析方法逃逸线程逃逸通过逃逸分析&#xff0c;编译器对代码的优化 锁优化 jvm 在加锁的过程中&#xff0c;会采用自旋、自适应、锁消除、锁粗化等优化手段来提升代码执行效率。 自旋锁和自适应自旋 现在大多的处理器都是…

易点易动设备管理系统:解决企业设备管理难题的利器

在现代企业中&#xff0c;设备管理是一个至关重要的环节。无论是制造业、物流业还是服务业&#xff0c;设备的高效管理对于企业的运营和竞争力都至关重要。然而&#xff0c;许多企业在设备管理方面面临着各种挑战。为了解决这些难题&#xff0c;易点易动设备管理系统应运而生。…

[vue]Echart使用手册

[vue]Echart使用手册 使用环境Echart的使用Echart所有组件和图表类型Echart 使用方法 使用环境 之前是在JQuery阶段使用Echart&#xff0c;直接引入Echart的js文件即可&#xff0c;现在是在vue中使用,不仅仅时echarts包&#xff0c;还需要安装vue-echarts&#xff1a; "…

Pycharm解释器的配置: System Intgerpreter 、Pipenv Environment、Virtualenv Environment

文章目录 前提1. 环境准备2. 了解虚拟环境 一、进入Interpreter设置页二、添加Interpreter1. 方式一2. 方式二 三、 System Interpreter四、 Pipenv Environment前提条件&#xff1a;详细步骤1&#xff09; 选择pipenv2&#xff09; 设置Base Interpreter3&#xff09; 设置Pip…

基于Java SSM框架实现人事员工考勤签到请假管理系统项目【项目源码+论文说明】

基于java的SSM框架实现人事员工考勤签到请假管理系统演示 摘要 在高速发展的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;人们对人事管理系统越来越重视&#xff0…

css图片属性,图片自适应

CSS 图片属性指南&#xff1a;background-size 和 object-fit 在前端开发中&#xff0c;使用图片是非常常见的。为了让图片在网页中显示得更好&#xff0c;CSS 提供了多种属性来调整和控制图片的大小和布局。其中&#xff0c;background-size 和 object-fit 是两个常用的属性&a…

制作系统安装盘教程——烧录Windows原版镜像

前言 本次教程不经过WinPE工具进行安装Windows原版镜像&#xff0c;而是直接把系统镜像文件直接烧录进U盘&#xff0c;这样做的好处是不经过WinPE安装Win系统的过程&#xff0c;避免有些带木马病毒的WinPE在安装系统的过程把木马病毒带进系统&#xff0c;从而导致文件泄漏。 开…

【JavaWeb学习笔记】13 - JSP浏览器渲染技术

JSP 一、JSP引入 1.JSP现状 1.目前主流的技术是前后端分离(比如: Spring Boot Vue/React),我们会讲的.[看一下] 2. JSP技术使用在逐渐减少&#xff0c;但使用少和没有使用是两个意思&#xff0c;一些老项目和中小公司还在使用JSP&#xff0c;工作期间,你很有可能遇到JSP …

【设计模式-2.5】创建型——建造者模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型设计模式中的最后一个&#xff0c;建造者模式&#xff1b; 入学报道 创建型模式&#xff0c;关注于对象的创建&#xff0c;建造者模式也不例外。假设现在有一个场景&#xff0c;高校开学&#xff0c;学生、教师、职工都…

12.21

一、注意事项 1.CtrlShiftT用于从jar中查找类&#xff0c;这个eclipse键盘快捷键可以帮助快速找到类 2.更新upm和rest遇到重复解决方法 把upm文件里面多出来的三行代码删掉&#xff0c;右击upm文件&#xff0c;点击小组&#xff0c;点击标记为已解决&#xff0c;点击OK&#x…

Web前端-JavaScript(对象)

文章目录 1.对象1.1 概念1.2 创建对象三种方式**对象字面量创建对象**&#xff1a;new Object创建对象构造函数创建对象 1.3 遍历对象 2.作用域1.1 概述1.2 全局作用域1.3 局部作用域1.4 JS没有块级作用域1.5 变量的作用域1.6 作用域链1.7 预解析 1.对象 1.1 概念 什么是对象 …

Solon 开源框架,单月下载突破 250 万!!!

Solon 是什么开源项目&#xff1f; 一个&#xff0c;Java 生态型应用开发框架。它从零开始构建&#xff0c;有自己的标准规范与开放生态&#xff08;历时六年&#xff0c;已有全球第二级别的生态规模&#xff09;。与其他框架相比&#xff0c;它解决了两个重要的痛点&#xff…

jetbrains idea 报错 java.lang.ClassNotFoundException 之后自动搜索包导入包

-- 搜索类所在的包 导入包 搜索包 mac环境 pom中右键或者 cmdn

shell 的错误处理和调试方法

简介 在我们写代码过程中&#xff0c;一般有两个阶段&#xff1a;调试阶段和试运行阶段。在调试阶段我们希望尽可能的输出日志&#xff0c;方便在出错的时候快速定位问题。在试运行阶段希望将日志标准化&#xff0c;且有些错误的日志是在预期内不想展示的时候如何处理&#xff…