当前位置: 首页 > 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…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析

MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录&#xff0c;这个目录下存放着许多可执行文件。与其他系统的可执行文件类似&#xff0c;这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中&#xff0c;用…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...