网关过滤器使用及其原理分析
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总结
新增功能点 讨论 学生和老师均可发布话题,话题发布后,在该课程的用户可以在发布话题的课程中点击讨论参与课程的讨论,并可以进行评论,回复评论,点赞操作。每个话题发布时可添加多个图片文件,参加讨论的话…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
