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 {

    // 配置全局过滤器
    @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());
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Centos7在安装Graylog时新安装MongoDB报错端口不监听服务不启动无法运行启动失败

由于虚拟机服务器上需要安装Graylog需要安装MongoDB&#xff0c;尝试官网下载安装包&#xff0c;和yum安装均无法正常启动&#xff0c;折腾了好几天&#xff0c;重装了十几次&#xff0c;网上搜索了很多很多资料&#xff0c;均无法正常运行&#xff0c;百度上搜索各种文档&…

内网穿透的应用-Docker本地部署青龙面板并实现公网远程访问管理界面

文章目录 一、前期准备本教程环境为&#xff1a;Centos7&#xff0c;可以跑Docker的系统都可以使用。本教程使用Docker部署青龙&#xff0c;如何安装Docker详见&#xff1a; 二、安装青龙面板三、映射本地部署的青龙面板至公网四、使用固定公网地址访问本地部署的青龙面板 青龙…

【数字图像处理】实验三 图像增强

图像增强 一、实验内容&#xff1a; 1&#xff0e; 熟悉和掌握利用Matlab工具进行数字图像的读、写、显示等数字图像处理基本步骤。 2&#xff0e; 熟练掌握各种图像增强的基本原理及方法。 3&#xff0e; 能够从深刻理解图像增强&#xff0c;并能够思考拓展到一定的应用领域。…

WPF组合控件TreeView+DataGrid之TreeView封装

&#xff08;关注博主后&#xff0c;在“粉丝专栏”&#xff0c;可免费阅读此文&#xff09; wpf的功能非常强大&#xff0c;很多控件都是原生的&#xff0c;但是要使用TreeViewDataGrid的组合&#xff0c;就需要我们自己去封装实现。 我们需要的效果如图所示&#x…

《代码整洁之道:程序员的职业素养》读后感

概述 工作即将满8年&#xff0c;如果算上2年实习的话&#xff0c;满打满算我已经走过将近10年的程序员编码生涯。关于Spring Boot知识点&#xff0c;关于微服务理论&#xff0c;也已经看过好几本书籍&#xff0c;看过十几篇技术Blog&#xff0c;甚至自己也写过相关技术Blog。 …

【计算机网络】TCP心跳机制、TCP粘包问题

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多计算机网络知识专栏&#xff1a;计算机网络&#x1f525; 给大家跳段…

案例125:基于微信小程序的个人健康数据管理系统的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

Nginx快速入门:nginx各类转发、代理配置详解|location、proxy_pass参数详解(五)

0. 引言 咱们上节讲解了nginx的负载均衡配置&#xff0c;但是还有很多其他的转发情况&#xff0c;包括不同路径转发至不同的业务服务&#xff0c;通配符识别路径转发等。 今天一起来学习nginx的转发配置 1. location模块的匹配模式 首先我们要了解nginx进行转发代理的核心在…

vue项目npm run build报错npm ERR! missing script: build(已解决)

vue项目npm run build报错npm ERR! missing script: build&#xff08;已解决&#xff09; 错误描述&#xff1a; 今天准备打包vue项目上线是出现下列错误&#xff1a; 找了很多解决方法都不行&#xff0c;最后打开自己的package.json文件发现&#xff1a;build后面多了个&a…

stable diffusion工作原理

目录 序言stable diffusion能做什么扩散模型正向扩散逆向扩散 如何训练逆向扩散 Stable Diffusion模型潜在扩散模型变分自动编码器图像分辨率图像放大为什么潜在空间可能存在&#xff1f;在潜在空间中的逆向扩散什么是 VAE 文件&#xff1f; 条件化(conditioning)文本条件化&am…

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

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

shell 的错误处理和调试方法

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

nodejs微信小程序+python+PHP个性化书籍推荐系统-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

JavaWeb笔记之前端开发JQuery

一、引言 1.1 概述 jQuery是一个快速、简洁的JavaScript代码库。jQuery设计的宗旨是“Write Less&#xff0c;Do More”&#xff0c;即倡导写更少的代码&#xff0c;做更多的事情。它封装JavaScript常用的功能代码&#xff0c;提供一种简便的 JavaScript操作方式&#xff0c…

宽带阻抗匹配的工程实现-第一步,端口驻波仿真

概要 ADS仿真&#xff0c;Matlab仿真&#xff0c;宽带阻抗匹配&#xff0c;smith圆图。 其实阻抗匹配我工作以来经常说&#xff0c;也经常做&#xff0c;但是基本上都是直接在印制板上进行调试。现在想先用仿真软件直接设计出来&#xff0c;才发现很多东西嘴上说容易&#xf…

JavaScript:函数

JavaScript&#xff1a;函数 函数的作用函数的声明和调用函数声明函数调用函数重复声明 函数传参传参语法参数默认值与参数数量问题传参数量过多传参数量太少参数默认值 函数的返回值函数表达式匿名函数立即执行函数 函数的作用 在我们编程过程中&#xff0c;会出现一种情况&a…

vue-内网,离线使用百度地图(地图瓦片图下载静态资源展示定位)

前言 最近发现很多小伙伴都在问内网怎么使用百度地图&#xff0c;或者是断网情况下能使用百度地图吗 后面经过一番研究&#xff0c;主要难点是&#xff0c;正常情况下我们是访问公网百度图片&#xff0c;数据&#xff0c;才能使用 内网时访问不了百度地图资源时就会使用不了&…

Ps 滤镜:油画

Ps菜单&#xff1a;滤镜/风格化/油画 Filter/Stylize/Oil Paint 油画 Oil Paint滤镜可以轻松地将照片转换为具有经典油画效果的图像。 原图 效果图 Photoshop 23.2 版本重新编写了油画滤镜&#xff0c;以更好地利用 macOS 和 Windows 上的本机 GPU 资源&#xff0c;从而提升了性…

gem5 garnet 拓扑结构之port: NI CPU ROUTER L1 L2

简介 有Crossbar&#xff0c;CrossbarGarnet&#xff0c;Mesh_*&#xff0c;MeshDirCorners_XY&#xff0c;Pt2Pt等拓扑结构&#xff0c;我们主要关注mesh-xy。参考是https://www.gem5.org/documentation/general_docs/ruby/interconnection-network/ MESI TWO LEVEL与 mesh …

SOLIDWORKS Flow Simulation升力仿真分析

仿真飞车起飞和飞机起飞的原理相同,当等质量的空气同时通过机翼上表面和下表面时,会在机翼上下方形成不同流速,空气通过机翼上表面时流速大&#xff0c;压强较小;通过下表面时流速较小,压强大。此时飞车会受一个向上的合力,即向上的升力,空气速度越快,升力越大,当升力大于飞车重…