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.hqwc.cn/news/291807.html

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

相关文章

​ SK Ecoplant借助亚马逊云科技,海外服务器为环保事业注入新活力

在当今全球面临着资源紧缺和环境挑战的大背景下&#xff0c;数字技术所依赖的海外服务器正成为加速循环经济转型的关键利器。然而&#xff0c;很多企业在整合数字技术到运营中仍然面临着一系列挑战&#xff0c;依然存在低效流程导致的不必要浪费。针对这一问题&#xff0c;SK E…

工具系列:PyCaret介绍_用外生变量单变量时间序列预测

工具系列&#xff1a;PyCaret介绍_用外生变量单变量时间序列预测 在这组笔记本中&#xff0c;我们将介绍使用外生变量进行建模。我们的行动计划如下&#xff1a; 对数据集进行探索性数据分析&#xff0c;以提取关于生成时间序列的过程的有价值的见解。 构建一个基准模型&…

元旦厦门游--ChatGPT/GPT4科研实践应用与AI绘图技术

2023年我们进入了AI2.0时代。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车&#xff0c;就有可能被淘汰在这个数字化时代&#xff0c;如何能高效地处理文本、文献查阅、PPT…

YOLOv8改进 | 主干篇 | 利用MobileNetV2替换Backbone(轻量化网络结构)

一、本文介绍 本文给大家带来的改进机制是MobileNetV2&#xff0c;其是专为移动和嵌入式视觉应用设计的轻量化网络结构。其在MobilNetV1的基础上采用反转残差结构和线性瓶颈层。这种结构通过轻量级的深度卷积和线性卷积过滤特征&#xff0c;同时去除狭窄层中的非线性&#xff…

力扣每日一题day37[113.路径总和ii]

给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22 输出&a…

lamda表达式(史上最全)

一、函数式接口 在jdk8中什么是函数式接口&#xff1a; 被FunctionalInterface注解修饰的。接口里边只有一个非default的方法。 满足以上2个条件的即为函数式接口&#xff0c;ps&#xff1a;即使一个接口没有FunctionalInterface修饰&#xff0c;但是满足2&#xff0c;那么这…

Netty Review - Netty与Protostuff:打造高效的网络通信

文章目录 概念PrePomServer & ClientProtostuffUtil 解读测试小结 概念 Pre 每日一博 - Protobuf vs. Protostuff&#xff1a;性能、易用性和适用场景分析 Pom <dependency><groupId>com.dyuproject.protostuff</groupId><artifactId>protostuff-…

打破Tomcat中的双亲委派机制:探讨与实践

目录 引言 1. 双亲委派机制概述 2. 打破双亲委派机制的场景 3. Tomcat中的类加载器体系 4. 打破双亲委派机制的方法 4.1 在catalina.properties中配置common.loader 4.2 在META-INF/context.xml中配置Loader元素 4.3 编写自定义的类加载器 5. 潜在的问题与解决方案 5…

Android13 Wifi启动流程分析

Android13 Wifi启动流程分析 文章目录 Android13 Wifi启动流程分析一、正常开关wifi 启动流程1、WifiManager2、WifiServiceImpl3、ActiveModeWarden4、ConcreteClientModeManager5、WifiNative6、WifiVendorHal7、HalDeviceManager8、wifi.cpp 二、重启设备时自动开启wifi流程…

关于LayUI弹出层表单提交两次问题

在弹出层点击 注册 按钮表单会提交两次 但是在单独页面点击是正常的 经过排查发现&#xff0c;问题在于主页面和注册页面重复引用了layui.js&#xff0c;页面要被渲染两次 <script src"../layui/layui.js"></script>

【IntelliJ IDEA】打开项目Git突然无法识别解决方案

这个问题也是我今天突然偶尔遇到的&#xff0c;当时没在意&#xff0c;项目打开之后又关闭&#xff0c;后来很久才又打开&#xff0c;发现项目明明有git版本控制的&#xff0c;咋突然开发工具右下角没有标识了&#xff0c;然后检查了一下git配置还报错了。 其实从图上我们可以看…

Debian在升级过程中报错

当我们在升级的过程中出现如下报错信息 报错信息如下所示&#xff1a; The following signatures couldnt be verified because the public key is not available: NO_PUBKEY ED444FF07D8D0BF6 W: GPG error: http://mirrors.jevincanders.net/kali kali-rolling InRelease: …