网关过滤器使用及其原理分析
1.网关过滤器介绍
网关过滤器的用途一般是修改请求或响应信息,例如编解码、Token验证、流量复制等
官方文档地址:Spring Cloud Gateway
网关过滤器分为GloablFilter、GatewayFilter及DefaultFilter
过滤器的执行顺序由Order决定,Order值越小,优先级越高,越先执行
1.1 GlobalFilter
自定义的GlobalFilter一般需要实现GlobalFilter和Order接口,官网的示例代码如下:
@Bean
public GlobalFilter customFilter() {return new CustomGlobalFilter();
}@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("custom global filter");return chain.filter(exchange);}@Overridepublic int getOrder() {return -1;}
}
配置了全局过滤器之后,当接收到匹配路由的请求时,会执行过滤
1.2 DefaultFilter
DefaultFilter也可以作用于所有路由,配置方式如下:
spring:cloud:gateway:default-filters:- AddRequestHeader=X-Request-Id,123456
1.3 GatewayFilter
官方提供了许多现成的GatewayFilter,可以直接使用,具体参考官方文档,RewritePath示例如下:
spring:cloud:gateway:routes:- id: rewritepath_routeuri: https://example.orgpredicates:- Path=/red/**filters:- RewritePath=/red/?(?<segment>.*), /$\{segment}
很多场景下需要自定义GatewayFilter,一般可以自定义GatewayFilterFactory继承自AbstractGatewayFilterFactory,名称必须由filter名称+GatewayFilterFactory组成,然后再实现apply方法中返回自定义的GatewayFilter对象,该对象一般需要实现GatewayFilter及Order接口,示例如下:
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> {public CustomGatewayFilterFactory() {super(CustomConfig.class);}@Overridepublic GatewayFilter apply(CustomConfig config) {return new CustomGatewayFilter(config);}
}
@Getter
@Setter
public class CustomConfig {private String desc;
}
@Slf4j
public class CustomGatewayFilter implements GatewayFilter, Ordered {// 配置类,对应yaml中的args部分private CustomConfig config;public CallbackGatewayFilter(CallbackConfig config) {this.config = config;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 执行过滤逻辑log.info("custom gateway filter:{}", config.getDesc);return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}
}
在配置文件中使用时,通过name属性指定filter名称,可以通过args传入自定义配置参数,这些参数会被封装到配置类CustomConfig中,例如:
spring:cloud:gateway:routes:- id: oldServeruri: lb://oldServerpredicates:- Path=/MyServer/**filters:# 通过name+GatewayFilterFactory找到对应的过滤器- name: Custom# 自定义过滤器配置args:desc: "this is a custom filter"
2.实际开发中的应用
2.1 缓存请求体全局过滤器
添加缓存请求体的全局过滤器,作用是避免后续获取body数据时报错Only one connection receive subscriber allowed,因为原始的body数据只能被订阅读取一次
@Slf4j
@Component
public class CacheRequestBodyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();HttpHeaders headers = request.getHeaders();long contentLength = headers.getContentLength();Optional<String> chunked = headers.toSingleValueMap().entrySet().stream().filter(entry -> entry.getKey().equalsIgnoreCase(HttpHeaders.TRANSFER_ENCODING.toLowerCase())).map(Map.Entry::getValue).filter(value -> value.equalsIgnoreCase("chunked")).findFirst();if (contentLength > 0 || chunked.isPresent()) {return readBody(exchange, chain);}return filterExchange(exchange, chain);}private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {byte[] bytes = new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);DataBufferUtils.release(dataBuffer);// Flux.defer()延迟创建Flux,直到有订阅者时才创建Flux<DataBuffer> cachedFlux = Flux.defer(() -> {DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);// 添加到exchange的requestBody属性中exchange.getAttributes().put("requestBody", new String(bytes));return Mono.just(buffer);});// 构建ServerHttpRequest的装饰器,重写getBody方法,避免出现body只能获取一次的问题ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic Flux<DataBuffer> getBody() {return cachedFlux;}};// 修改exchange中的请求对象ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();return filterExchange(mutatedExchange, chain);});}@Overridepublic int getOrder() {// 最高优先级return HIGHEST_PRECEDENCE;}private static Mono<Void> filterExchange(ServerWebExchange exchange, GatewayFilterChain chain) {return ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders()).bodyToMono(String.class).doOnNext(objectValue -> log.info("[GatewayContext]Read body Success")).then(chain.filter(exchange));}
}
2.2 请求加解密过滤器
在实际开发中,网关接收到的请求内容时常是加密的,需要解密,而响应的内容有时需要加密,这种场景下可以通过自定义网关过滤器实现
1)创建加解密过滤器工厂类
@Component
public class EncryptDecryptGatewayFilterFactory extends AbstractGatewayFilterFactory<EncryptDecryptConfig> {public EncryptDecryptGatewayFilterFactory() {super(EncryptDecryptConfig.class);}@Overridepublic GatewayFilter apply(EncryptDecryptConfig config) {return new EncryptDecryptFilter(config);}
}
2)创建加解密过滤器
@Slf4j
public class EncryptDecryptFilter implements GatewayFilter, Ordered {private final EncryptDecryptConfig config;public EncryptDecryptFilter(EncryptDecryptConfig config) {this.config = config;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 创建一个表达式对象ExchangeExpression expression = new ExchangeExpression(exchange);// 如果没有配置待解密表达式,则无需解密List<String> encodedFieldList = config.getEncodedField();if (!CollectionUtils.isEmpty(encodedFieldList)) {String encodeData = null;for (String encodedField : encodedFieldList) {// 执行表达式,获取待解密的字符串encodeData = (String) expression.evalNoException(encodedField);if (!StringUtils.isEmpty(encodeData)){break;}}String bodyData;if (StringUtils.hasText(encodeData)) {// 执行解密,这里使用最简单的Base64进行加解密,可以根据实际情况换成其他的加解密算法,这里仅演示网关的作用bodyData = Base64.decodeStr(encodeData);// 假设请求体是JSON格式,获取解密后的内容,设置到上下文中,这样后面的过滤器可以方便使用Map<String, Object> bodyMap = JSONUtil.parseObj(bodyData);for (Map.Entry<String, Object> entry : bodyMap.entrySet()) {exchange.getAttributes().put(entry.getKey(), entry.getValue());}// 重新构造解密后的exchange内容ExchangeConfig exchangeConfig = ExchangeConfig.builder().bodyObject(bodyData).bodyContentType(config.getRequestContentType()).header((Map<String, String>) expression.getField("header")).param((Map<String, String>) expression.getField("param")).build();exchange = ServerWebExchangeUtil.rebuildRequest(exchange, exchangeConfig);}}// 根据配置判断响应是否需要加密boolean encrypt = Boolean.TRUE.equals(config.getCrypt());if (encrypt) {// 如果需要加密,则修改响应内容return chain.filter(exchange.mutate().response(buildResponse(exchange)).build());}// 执行下一个过滤器return chain.filter(exchange);}@Overridepublic int getOrder() {// 设置过滤器执行顺序,解密过滤器一般优先级最高,order必须小于-1,否则writeWith方法不生效,原因是NettyWriteResponseFilter的order为-1return config.getOrder() == null ? Integer.MIN_VALUE + 1 : config.getOrder();}private ServerHttpResponseDecorator buildResponse(ServerWebExchange exchange) {ServerHttpResponse originalResponse = exchange.getResponse();return new ServerHttpResponseDecorator(originalResponse) {@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = super.getHeaders();相关文章:
网关过滤器使用及其原理分析
1.网关过滤器介绍 网关过滤器的用途一般是修改请求或响应信息,例如编解码、Token验证、流量复制等 官方文档地址:Spring Cloud Gateway 网关过滤器分为GloablFilter、GatewayFilter及DefaultFilter 过滤器的执行顺序由Order决定,Order值越小,优先级越高,越先执行 1.1…...
jiebaNET中文分词器
最近我接手了一个有趣的需求,需要对用户评价进行分词,进行词频统计和情绪分析,并且根据词频权重制成词云图以供后台数据统计,于是我便引入了jieba分词器,但是我发现网上关于jiebaNET相关文档实在太少了,甚至连配置文件…...
springboot3项目练习详细步骤(第四部分:文件上传、登录优化、多环境开发)
目录 本地文件上传 接口文档 业务实现 登录优化 SpringBoot集成redis 实现令牌主动失效机制 多环境开发 本地文件上传 接口文档 业务实现 创建FileUploadController类并编写请求方法 RestController public class FileUploadController {PostMapping("/upload&…...
视觉里程计的融合方法及优缺点分析
视觉里程计是视觉slam的一部分,即前端部分,用于前端跟踪并建立局部地图。多用于重定位或辅助定位,常用的有特征点法,光流法和直接法,其区别和优缺点如下。 特征点法,需要计算特征点和描述子,计算…...
SQL常用基础语句(一)-- FGHIJ开头
GROUP BY GROUP BY语法可以根据给定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表。在GROUP BY子句后面包含了一个HAVING子句,HAVING类似于WHERE,(唯一的差别是WHERE过滤行,HAVING过滤组࿰…...
大语言模型量化方法对比:GPTQ、GGUF、AWQ 包括显存和速度
GPTQ: Post-Training Quantization for GPT Models GPTQ是一种4位量化的训练后量化(PTQ)方法,主要关注GPU推理和性能。 该方法背后的思想是,尝试通过最小化该权重的均方误差将所有权重压缩到4位。在推理过程中,它将动态地将其权重去量化为f…...
实现本地访问云主机,以及在云主机搭建FTP站点
前言 云计算是一种基于互联网的计算模式,通过网络提供按需访问的计算资源和服务。核心概念是把计算能力视作一种公共资源,用户可以根据自身需求动态分配和管理这些资源。 云主机 ECS (Elastic Compute Server)是一种按需获取的云端服务器,提…...
存储+调优:存储-Cloud
存储调优:存储-Cloud Master Server 配置: IP192.168.1.254 useradd mfs tar zxf mfs-1.6.11.tar.gz.gz cd mfs-1.6.11 ./configure --prefix/usr --sysconfdir/etc --localstatedir/var/lib --with-default-usermfs --with-default-groupmfs --disabl…...
海山数据库(He3DB)线程池方案详解
前言 对于应用开发人员来说肯定听说过连接池,却不一定听说过线程池,虽然二者都是池化的概念,但还是有所不同的: 连接池面向的是数据库连接,是针对数据库Client侧的优化。连接池可将数据库连接数固定在一定范围内&#…...
K8s 高级调度
文章目录 K8s 高级调度CronJobinitContainerTaint 和 Toleration污点(Taint)容忍(Toleration) AffinityNodeAffinityPodAnffinity 和 PodAntiAffinity 总结 K8s 高级调度 CronJob 在 k8s 中周期性运行计划任务,与 li…...
数据猿携手IDC Directions 2024:探索中国ICT市场新趋势
大数据产业创新服务媒体 ——聚焦数据 改变商业 近日,ICT业界人士、行业数字化专家,以及来自投资机构、协会、智库的近500位嘉宾聚首北京,参加了IDC Directions 2024:中国ICT市场趋势论坛(北京站)活动。活…...
前端开发工程师——ajax
express框架 终端输入 npm init --yes npm i express 请求报文/响应报文 // 1.引入express const express require(express);// 2.创建应用对象 const app express();// 3.创建路由规则 // request:是对请求报文的封装 // response:是对响应报文的封装 app.get(…...
uni-app项目在微信开发者工具打开时报错[ app.json 文件内容错误] app.json: 在项目根目录未找到 app.json
uni-app项目在微信开发者工具打开时报错[ app.json 文件内容错误] app.json: 在项目根目录未找到 app.json 出现这个问题是因为打开的文件地址不对,解决这个问题首先我们要查看是否有unpackage文件夹,如果有,项目直接指向unpackage\dist\dev\…...
最大连续1的个数(滑动窗口)
算法原理: 这道题大眼一看是关于翻转多少个0的问题,但是,如果你按照这种思维去做题,肯定不容易。所以我们要换一种思维去做,这种思维不是一下就能想到的,所以想不到也情有可原。 题目是:给定一…...
Spring Cloud 框架的应用详解
Spring Cloud 框架的应用详解 Spring Cloud 是一个基于 Spring Boot 的微服务架构开发工具,它提供了一系列工具用于快速构建分布式系统中的常见模式,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、选举、分布式会话和集群状态管理…...
C语言 数组——向函数传递数组
目录 把数组传给函数(Passing Arrays to Functions) 向函数传递一维数组 向函数传递二维数组 数组在学生成绩管理中的应用 例:计算每个学生的平均分 把数组传给函数(Passing Arrays to Functions) 向函数传递一维…...
数据链路层简单介绍
mac地址(物理地址) mac地址和ip地址,目的都是为了区分网络上的不同设备的,在最开始的时候,mac地址和ip地址是两伙人,独立各自提出的,ip地址是4个字节(早都不够用了)&…...
【软考】设计模式之装饰器模式
目录 1. 说明2. 应用场景3. 结构图4. 构成5. 适用性6. 优点7. 缺点8. java示例 1. 说明 1.动态地给一个对象添加一些额外的职责。2.Decorator Pattern。3.就增加功能而言,装饰器模式比生成子类更加灵活。4.一种在不改变现有对象结构的情况下,动态地给对…...
网络编程day6
IO多路复用的原理 IO多路复用是一种通过同时监视多个文件描述符(通常是套接字)来处理多个I/O操作的机制。它的原理可以简单概括为: 单线程处理多个I/O操作:传统的I/O模型中,每个I/O操作都需要一个线程或进程来处理。而…...
5.23总结
新增功能点 讨论 学生和老师均可发布话题,话题发布后,在该课程的用户可以在发布话题的课程中点击讨论参与课程的讨论,并可以进行评论,回复评论,点赞操作。每个话题发布时可添加多个图片文件,参加讨论的话…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
高效的后台管理系统——可进行二次开发
随着互联网技术的迅猛发展,企业的数字化管理变得愈加重要。后台管理系统作为数据存储与业务管理的核心,成为了现代企业不可或缺的一部分。今天我们要介绍的是一款名为 若依后台管理框架 的系统,它不仅支持跨平台应用,还能提供丰富…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
