当前位置: 首页 > news >正文

SpringCloudGateway网关实战(三)

SpringCloudGateway网关实战(三)

上一章节我们讲了gateway的内置过滤器Filter,本章节我们来讲讲全局过滤器。

自带全局过滤器

在实现自定义全局过滤器前, spring-cloud-starter-gateway依赖本身就自带一些全局过滤器,我们举些比较常用的例子:

  1. NettyRoutingFilter:该过滤器使用Netty作为底层的HTTP客户端,负责将请求转发到下游服务。
  2. RouteToRequestUrlFilter:该过滤器将根据路由配置中的URI信息,将请求转发到指定的URL。
  3. WebsocketRoutingFilter:该过滤器用于处理WebSocket协议的请求转发。
  4. GatewayMetricsFilter:该过滤器用于收集网关的基本性能指标数据,例如请求的数量、响应时间等。

自定义全局过滤器

自定义全局过滤器需要实现GlobalFilter接口和Ordered接口。

AuthFilter

token验证全局过滤器

引入依赖:

        <!-- Jwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>

具体代码:

@Component
public class AuthFilter implements GlobalFilter, Ordered {private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);@Autowiredprivate IgnoreWhiteProperties ignoreWhite;@Autowiredpublic RedisTemplate redisTemplate;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();ServerHttpRequest.Builder mutate = request.mutate();// 跳过白名单String url = request.getURI().getPath();if (com.smallred.gateway.utils.StringUtils.matches(url, ignoreWhite.getWhites())) {return chain.filter(exchange);}// 检查令牌是否存在String token = getToken(request);if (StringUtils.isEmpty(token)) {return unauthorizedResponse(exchange, "令牌不能为空!");}//Claims claims = JwtUtils.parseToken(token);if (claims == null) {return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");}// 判断登录状态String userKey = JwtUtils.getUserKey(claims);Boolean islogin = redisTemplate.hasKey(getTokenKey(userKey));if (!islogin) {return unauthorizedResponse(exchange, "登录状态已过期!");}// 验证用户信息String userId = JwtUtils.getUserId(claims);String userName = JwtUtils.getUserName(claims);if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {return unauthorizedResponse(exchange, "令牌验证失败");}// 设置用户信息到请求addHeader(mutate, "user_key", userKey);addHeader(mutate, "user_id", userId);addHeader(mutate, "username", userName);// 内部请求来源参数清除removeHeader(mutate, "from-source");return chain.filter(exchange.mutate().request(mutate.build()).build());}/***  添加头*/private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {if (value == null) {return;}String valueStr = value.toString();String valueEncode = urlEncode(valueStr);mutate.header(name, valueEncode);}/***  删除头*/private void removeHeader(ServerHttpRequest.Builder mutate, String name) {mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();}/***  获取缓存key*/private String getTokenKey(String token) {return "login_tokens:" + token;}/***  获取请求token*/private String getToken(ServerHttpRequest request) {String token = request.getHeaders().getFirst("Authorization");// 如果前端设置了令牌前缀,则裁剪掉前缀if (StringUtils.isNotEmpty(token) && token.startsWith("Bearer")) {token = token.replaceFirst("Bearer", StringUtils.EMPTY);}return token;}/*** 内容编码** @param str 内容* @return 编码后的内容*/public static String urlEncode(String str){try{return URLEncoder.encode(str, "UTF-8");}catch (UnsupportedEncodingException e){return StringUtils.EMPTY;}}/***  验证失败返回*/private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, 401);}@Overridepublic int getOrder() {return -200;}
}

依赖类

白名单:

@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties {private List<String> whites = new ArrayList<>();public List<String> getWhites() {return whites;}public void setWhites(List<String> whites) {this.whites = whites;}
}

白名单配置:

# 安全配置
security:ignore:whites:- /auth/logout- /auth/login- /auth/register- /*/v2/api-docs- /csrf

StringUtils类:

public class StringUtils {/** 空字符串 */private static final String NULLSTR = "";public static boolean matches(String str, List<String> strs){if (isEmpty(str) || isEmpty(strs)){return false;}for (String pattern : strs){if (isMatch(pattern, str)){return true;}}return false;}public static boolean isEmpty(String str){return isNull(str) || NULLSTR.equals(str.trim());}public static boolean isEmpty(Collection<?> coll){return isNull(coll) || coll.isEmpty();}public static boolean isNull(Object object){return object == null;}public static boolean isMatch(String pattern, String url){AntPathMatcher matcher = new AntPathMatcher();return matcher.match(pattern, url);}}

ServletUtils类:

public class ServletUtils {public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code){return webFluxResponseWriter(response, HttpStatus.OK, value, code);}public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code){return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);}public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code){response.setStatusCode(status);response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);R<?> result = R.fail(code, value.toString());DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());return response.writeWith(Mono.just(dataBuffer));}}

JwtUtils类:

public class JwtUtils {public static String secret = "abcdefghijklmnopqrstuvwxyz";/*** 从数据声明生成令牌** @param claims 数据声明* @return 令牌*/public static String createToken(Map<String, Object> claims){String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();return token;}/*** 从令牌中获取数据声明** @param token 令牌* @return 数据声明*/public static Claims parseToken(String token){return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}/*** 根据令牌获取用户标识** @param token 令牌* @return 用户ID*/public static String getUserKey(String token){Claims claims = parseToken(token);return getValue(claims, "user_key");}/*** 根据令牌获取用户标识** @param claims 身份信息* @return 用户ID*/public static String getUserKey(Claims claims){return getValue(claims, "user_key");}/*** 根据令牌获取用户ID** @param token 令牌* @return 用户ID*/public static String getUserId(String token){Claims claims = parseToken(token);return getValue(claims, "user_id");}/*** 根据身份信息获取用户ID** @param claims 身份信息* @return 用户ID*/public static String getUserId(Claims claims){return getValue(claims, "user_id");}/*** 根据令牌获取用户名** @param token 令牌* @return 用户名*/public static String getUserName(String token){Claims claims = parseToken(token);return getValue(claims, "username");}/*** 根据身份信息获取用户名** @param claims 身份信息* @return 用户名*/public static String getUserName(Claims claims){return getValue(claims, "username");}/*** 根据身份信息获取键值** @param claims 身份信息* @param key 键* @return 值*/public static String getValue(Claims claims, String key){return toStr(claims.get(key), "");}public static String toStr(Object value, String defaultValue){if (null == value){return defaultValue;}if (value instanceof String){return (String) value;}return value.toString();}}

IpAddressFilter

根据请求记录ip地址日志:

@Component
public class IpAddressFilter implements GlobalFilter, Ordered {public static final Map<String, AtomicInteger> CACHE = new ConcurrentHashMap<>();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {InetSocketAddress host = exchange.getRequest().getHeaders().getHost();if (host == null || host.getHostName() == null) {exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);return exchange.getResponse().setComplete();}String hostName = host.getHostName();AtomicInteger count = CACHE.getOrDefault(hostName, new AtomicInteger(0));count.incrementAndGet();CACHE.put(hostName, count);System.out.println("IP地址:" + hostName + ",访问次数:" + count.intValue());return chain.filter(exchange);}@Overridepublic int getOrder() {return 101;}
}

相关文章:

SpringCloudGateway网关实战(三)

SpringCloudGateway网关实战&#xff08;三&#xff09; 上一章节我们讲了gateway的内置过滤器Filter&#xff0c;本章节我们来讲讲全局过滤器。 自带全局过滤器 在实现自定义全局过滤器前&#xff0c; spring-cloud-starter-gateway依赖本身就自带一些全局过滤器&#xff0…...

08在MyBatis-Plus中配置多数据源

配置多数据源 模拟多库场景 适用于多种场景: 多库(操作的表分布在不同数据库当中),读写分离(有的数据库负责查询的功能,有的数据库负责增删该的功能),一主多从,混合模式等 第一步: 模拟多库,在mybatis_plus数据库中创建user表,在mybatis_plus_1数据库中创建product表 --创建…...

Centos8安装docker并配置Kali Linux图形化界面

鉴于目前网上没有完整的好用的docker安装kali桌面连接的教程&#xff0c;所以我想做一个。 准备工作 麻了&#xff0c;这服务器供应商提供的镜像是真的纯净&#xff0c;纯净到啥都没有。 问题一&#xff1a;Centos8源有问题 Error: Failed to download metadata for repo ap…...

游戏开发初等数学基础

凑数图() 立体图形面积体积 1. 立方体&#xff08;Cube&#xff09;: 表面积公式: 6 a 2 6a^2 6a2 &#xff08;其中 a a a 是边长&#xff09;。体积公式: a 3 a^3 a3 &#xff08;其中 a a a 是边长&#xff09;。 2. 球体&#xff08;Sphere&#xff09;: 表面积公…...

svg图片代码data:image/svg+xml转png图片方法

把代码保存为html格式的文件中,用浏览器访问,即可右键保存 从AI软件或其它网站得到svg图片代码后,把他复制到下面源码上 注意:src""图片地址中,一些参数的含义 d‘这里是图片代码数据’ viewBox是图片显示区域,宽,高等 fill%23000000’这里表示颜色 ,后面6位0表示黑色…...

解决问题:Replace `‘vue‘;⏎` with `“vue“;`

使用vscode写vue文件的问题&#xff1a; Replace vue;⏎ with "vue"; error Replace v-model:value"xxx"placeholder"inputsearch prettier/prettier 7:38 error Insert ⏎ potentially fixable with the --fix option 原因&#xff1a;格式问题&a…...

ThinkPHP 5.0通过composer升级到5.1,超级简单

事情是这样的&#xff0c;我实现一个验证码登录的功能&#xff0c;但是这个验证码的包提示tp5的版本可以是5.1.1、5.1.2、5.1.3。但我使用的是5.0&#xff0c;既然这样&#xff0c;那就升个级呗&#xff0c;百度了一下&#xff0c;结果发现大部分都是讲先备份application和修改…...

计算机竞赛 多目标跟踪算法 实时检测 - opencv 深度学习 机器视觉

文章目录 0 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习多目标跟踪 …...

一文了解大模型工作原理——以ChatGPT为例

文章目录 写在前面1.Tansformer架构模型2.ChatGPT原理3.提示学习与大模型能力的涌现3.1 提示学习3.2 上下文学习3.3 思维链 4.行业参考建议4.1 拥抱变化4.2 定位清晰4.3 合规可控4.4 经验沉淀 写在前面 2022年11月30日&#xff0c;ChatGPT模型问世后&#xff0c;立刻在全球范围…...

CPP-Templates-2nd--第十九章 萃取的实现 19.7---

目录 19.7 其它的萃取技术 19.7.1 If-Then-Else 19.7.2 探测不抛出异常的操作 19.7.3 萃取的便捷性&#xff08;Traits Convenience&#xff09; 别名模板和萃取&#xff08;Alias Templates And Traits) 变量模板和萃取&#xff08;Variable Templates and Traits&…...

python 采用selenium+cookies 获取登录后的网页

百度网页由于需要登陆手机短信验证。比较麻烦 这里我采用先人工登录百度账号&#xff0c;然后将百度账号的相关cookies保存下来 然后采用selenium动态登录网页 整体代码如下 from selenium import webdriverimport timeoptions webdriver.ChromeOptions()options.add_argu…...

【测试开发】答疑篇 · 什么是软件测试

【测试开发】答疑篇 文章目录 【测试开发】答疑篇1. 生活中的测试2. 什么是软件测试3. 为什么要有测试/没有测试行不行4. 软件测试和软件开发的区别5. 软件测试和软件调试之间的区别6. 软件测试的岗位7. 优秀测试人员具备的素质 【测试开发】答疑篇 软件不一定是桌面应用&#…...

深入解析顺序表:揭开数据结构的奥秘,掌握顺序表的精髓

&#x1f493; 博客主页&#xff1a;江池俊的博客⏩ 收录专栏&#xff1a;数据结构探索&#x1f449;专栏推荐&#xff1a;✅C语言初阶之路 ✅C语言进阶之路&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f525;编译环境&#xff1a;Visual Studio 2022&#x1f38…...

数据风险量化评估方案

一、企业面临数据安全的痛点 1、企业缺少清晰的数据安全意识 各部门重视度不够&#xff0c;缺少主动数据安全管控意识。数据安全管控架构不清晰&#xff0c;职责划分不明确。对数据安全管控认识不全面、不深刻。工作人员对于所持有的数据缺乏概念&#xff0c;导致数据的价值无…...

EasyAVFilter代码示例之将视频点播文件转码成HLS(m3u8+ts)视频点播格式

以下是一套完整的视频点播功能开发源码&#xff0c;就简简单单几行代码&#xff0c;就可以完成原来ffmpeg很复杂的视频点播转码调用流程&#xff0c;而且还可以集成在自己的应用程序中调用&#xff0c;例如java、php、cgo、c、nodejs&#xff0c;不需要再单独一个ffmpeg的进程来…...

day-50 代码随想录算法训练营(19)动态规划 part 11

123.买卖股票的最佳时机||| 分析&#xff1a;只能买卖两次&#xff0c;就是说有五个状态&#xff1a; 没有买过第一次买入第一次卖出第二次买入第二次卖出 思路&#xff1a;二维数组&#xff0c;记录五个状态 1.dp存储&#xff1a;dp[i][1] 第一次买入 dp[i][2] 第一次卖…...

自定义权限指令与防止连点指令

1.权限指令 // 注册一个全局自定义权限指令 v-permission Vue.directive(permission, {inserted: function(el, binding, vnode) {const {value} binding; // 指令传的值// user:edit:phone,sysData:sampleconst permissions [user:edit:address, sysData:entrust, sysData:…...

UE5、CesiumForUnreal实现瓦片坐标信息图层效果

文章目录 1.实现目标2.实现过程2.1 原理简介2.2 cesium-native改造2.3 CesiumForUnreal改造2.4 运行测试3.参考资料1.实现目标 参考CesiumJs的TileCoordinatesImageryProvider,在CesiumForUnreal中也实现瓦片坐标信息图层的效果,便于后面在调试地形和影像瓦片的加载调度等过…...

PostgreSQL执行计划

1. EXPLAIN命令 1)PostgreSQL中EXPLAIN命令的语法格式: postgres# \h explain Command: EXPLAIN Description: show the execution plan of a statement Syntax: EXPLAIN [ ( option [, ...] ) ] statement EXPLAIN [ ANALYZE ] [ VERBOSE ] statementwhere option can be…...

【2023 睿思芯科 笔试题】~ 题目及参考答案

文章目录 1. 题目 & 答案单选题编程题问题1&#xff1a;解析1&#xff1a;问题2&#xff1a;解析2&#xff1a; 声明 名称如标题所示&#xff0c;希望大家正确食用&#xff08;点赞转发评论&#xff09; 本次笔试题以两种形式考察的&#xff0c;分别是&#xff1a;选择题&a…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...