SpringCloudGateway网关实战(三)
SpringCloudGateway网关实战(三)
上一章节我们讲了gateway的内置过滤器Filter,本章节我们来讲讲全局过滤器。
自带全局过滤器
在实现自定义全局过滤器前, spring-cloud-starter-gateway依赖本身就自带一些全局过滤器,我们举些比较常用的例子:
- NettyRoutingFilter:该过滤器使用Netty作为底层的HTTP客户端,负责将请求转发到下游服务。
- RouteToRequestUrlFilter:该过滤器将根据路由配置中的URI信息,将请求转发到指定的URL。
- WebsocketRoutingFilter:该过滤器用于处理WebSocket协议的请求转发。
- 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网关实战(三) 上一章节我们讲了gateway的内置过滤器Filter,本章节我们来讲讲全局过滤器。 自带全局过滤器 在实现自定义全局过滤器前, spring-cloud-starter-gateway依赖本身就自带一些全局过滤器࿰…...
08在MyBatis-Plus中配置多数据源
配置多数据源 模拟多库场景 适用于多种场景: 多库(操作的表分布在不同数据库当中),读写分离(有的数据库负责查询的功能,有的数据库负责增删该的功能),一主多从,混合模式等 第一步: 模拟多库,在mybatis_plus数据库中创建user表,在mybatis_plus_1数据库中创建product表 --创建…...
Centos8安装docker并配置Kali Linux图形化界面
鉴于目前网上没有完整的好用的docker安装kali桌面连接的教程,所以我想做一个。 准备工作 麻了,这服务器供应商提供的镜像是真的纯净,纯净到啥都没有。 问题一:Centos8源有问题 Error: Failed to download metadata for repo ap…...
游戏开发初等数学基础
凑数图() 立体图形面积体积 1. 立方体(Cube): 表面积公式: 6 a 2 6a^2 6a2 (其中 a a a 是边长)。体积公式: a 3 a^3 a3 (其中 a a a 是边长)。 2. 球体(Sphere): 表面积公…...
svg图片代码data:image/svg+xml转png图片方法
把代码保存为html格式的文件中,用浏览器访问,即可右键保存 从AI软件或其它网站得到svg图片代码后,把他复制到下面源码上 注意:src""图片地址中,一些参数的含义 d‘这里是图片代码数据’ viewBox是图片显示区域,宽,高等 fill%23000000’这里表示颜色 ,后面6位0表示黑色…...
解决问题:Replace `‘vue‘;⏎` with `“vue“;`
使用vscode写vue文件的问题: Replace vue;⏎ with "vue"; error Replace v-model:value"xxx"placeholder"inputsearch prettier/prettier 7:38 error Insert ⏎ potentially fixable with the --fix option 原因:格式问题&a…...
ThinkPHP 5.0通过composer升级到5.1,超级简单
事情是这样的,我实现一个验证码登录的功能,但是这个验证码的包提示tp5的版本可以是5.1.1、5.1.2、5.1.3。但我使用的是5.0,既然这样,那就升个级呗,百度了一下,结果发现大部分都是讲先备份application和修改…...
计算机竞赛 多目标跟踪算法 实时检测 - opencv 深度学习 机器视觉
文章目录 0 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 深度学习多目标跟踪 …...
一文了解大模型工作原理——以ChatGPT为例
文章目录 写在前面1.Tansformer架构模型2.ChatGPT原理3.提示学习与大模型能力的涌现3.1 提示学习3.2 上下文学习3.3 思维链 4.行业参考建议4.1 拥抱变化4.2 定位清晰4.3 合规可控4.4 经验沉淀 写在前面 2022年11月30日,ChatGPT模型问世后,立刻在全球范围…...
CPP-Templates-2nd--第十九章 萃取的实现 19.7---
目录 19.7 其它的萃取技术 19.7.1 If-Then-Else 19.7.2 探测不抛出异常的操作 19.7.3 萃取的便捷性(Traits Convenience) 别名模板和萃取(Alias Templates And Traits) 变量模板和萃取(Variable Templates and Traits&…...
python 采用selenium+cookies 获取登录后的网页
百度网页由于需要登陆手机短信验证。比较麻烦 这里我采用先人工登录百度账号,然后将百度账号的相关cookies保存下来 然后采用selenium动态登录网页 整体代码如下 from selenium import webdriverimport timeoptions webdriver.ChromeOptions()options.add_argu…...
【测试开发】答疑篇 · 什么是软件测试
【测试开发】答疑篇 文章目录 【测试开发】答疑篇1. 生活中的测试2. 什么是软件测试3. 为什么要有测试/没有测试行不行4. 软件测试和软件开发的区别5. 软件测试和软件调试之间的区别6. 软件测试的岗位7. 优秀测试人员具备的素质 【测试开发】答疑篇 软件不一定是桌面应用&#…...
深入解析顺序表:揭开数据结构的奥秘,掌握顺序表的精髓
💓 博客主页:江池俊的博客⏩ 收录专栏:数据结构探索👉专栏推荐:✅C语言初阶之路 ✅C语言进阶之路💻代码仓库:江池俊的代码仓库🔥编译环境:Visual Studio 2022Ἰ…...
数据风险量化评估方案
一、企业面临数据安全的痛点 1、企业缺少清晰的数据安全意识 各部门重视度不够,缺少主动数据安全管控意识。数据安全管控架构不清晰,职责划分不明确。对数据安全管控认识不全面、不深刻。工作人员对于所持有的数据缺乏概念,导致数据的价值无…...
EasyAVFilter代码示例之将视频点播文件转码成HLS(m3u8+ts)视频点播格式
以下是一套完整的视频点播功能开发源码,就简简单单几行代码,就可以完成原来ffmpeg很复杂的视频点播转码调用流程,而且还可以集成在自己的应用程序中调用,例如java、php、cgo、c、nodejs,不需要再单独一个ffmpeg的进程来…...
day-50 代码随想录算法训练营(19)动态规划 part 11
123.买卖股票的最佳时机||| 分析:只能买卖两次,就是说有五个状态: 没有买过第一次买入第一次卖出第二次买入第二次卖出 思路:二维数组,记录五个状态 1.dp存储: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:解析1:问题2:解析2: 声明 名称如标题所示,希望大家正确食用(点赞转发评论) 本次笔试题以两种形式考察的,分别是:选择题&a…...
保姆级教程:用Sigrity PowerSI提取5GHz内单端S参数(附DDR4仿真实例)
从零掌握Sigrity PowerSI:5GHz单端S参数提取与DDR4实战解析 在高速PCB设计中,信号完整性问题往往成为工程师的"隐形杀手"。当DDR4内存接口速率突破2400MHz时,传统时域分析方法已难以捕捉信号在传输过程中的微妙变化。散射参数&…...
基于插件化架构的OBS实时音乐信息集成系统技术解析
基于插件化架构的OBS实时音乐信息集成系统技术解析 【免费下载链接】tuna Song information plugin for obs-studio 项目地址: https://gitcode.com/gh_mirrors/tuna1/tuna Tuna是一款面向OBS Studio的高性能插件化实时音乐信息集成系统,采用模块化架构设计&…...
Dism++终极指南:5步彻底解决Windows系统卡顿和臃肿问题
Dism终极指南:5步彻底解决Windows系统卡顿和臃肿问题 【免费下载链接】Dism-Multi-language Dism Multi-language Support & BUG Report 项目地址: https://gitcode.com/gh_mirrors/di/Dism-Multi-language 你是否曾为Windows系统越来越慢而烦恼…...
告别串口助手:用STM32CubeIDE和HAL库,手把手教你打造自己的IAP上位机(附源码)
从零构建STM32 IAP上位机:C#实战与协议解析全指南 在嵌入式开发中,IAP(In Application Programming)技术为设备固件升级提供了极大便利,但一个稳定可靠的上位机软件往往是整个流程中最薄弱的环节。市面上通用的串口助手…...
多波束声呐接收机与信号处理算法【附程序】
✨ 长期致力于多通道声呐接收机、电路设计、FPGA、数字信号处理、波束形成研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)小型化96通道接收机硬件电路…...
解锁网络音视频传输:DistroAV插件从零构建高效工作流
解锁网络音视频传输:DistroAV插件从零构建高效工作流 【免费下载链接】obs-ndi DistroAV (formerly OBS-NDI): NDI integration for OBS Studio 项目地址: https://gitcode.com/gh_mirrors/ob/obs-ndi 在现代直播制作和视频协作场景中,跨设备高质…...
告别枯燥理论:用Verilog在FPGA上实现一个可交互的I2C温度传感器从机
从零构建FPGA上的智能温度传感器:Verilog I2C从机实战指南 当你想在FPGA上连接一个温度传感器时,市面上常见的I2C传感器如LM75似乎是个简单选择——但你是否想过,用Verilog自己实现一个会是什么体验?本文将带你从协议层开始&#…...
如何自定义 LangGraph 的 State Schema 以支持复杂业务数据流
标题选项 《LangGraph实战进阶:自定义State Schema搞定复杂业务数据流全指南》 《从零搞定LangGraph复杂工作流:State Schema自定义从原理到落地》 《告别简单Demo:自定义LangGraph State Schema支撑企业级复杂数据流》 《LangGraph核心原理解锁:State Schema自定义设计思路…...
蓝牙6.0 Channel Sounding 基于接入地址的定时估计原理
基于接入地址的定时估计 先看下core spec的描述:蓝牙Core Spec Vol 6 Part H中 3.2节「基于接入地址的定时估计」,它定义了两种用于CS_SYNC包到达时间(ToA)估计的方法,是RTT测距的基础定时方案。下面我逐段拆解&#x…...
如何安全导出浏览器Cookie:本地化工具的完整使用教程
如何安全导出浏览器Cookie:本地化工具的完整使用教程 【免费下载链接】Get-cookies.txt-LOCALLY Get cookies.txt, NEVER send information outside. 项目地址: https://gitcode.com/gh_mirrors/ge/Get-cookies.txt-LOCALLY 你是否曾需要将浏览器Cookie导出到…...
