gateway中对返回的数据进行处理

gateway中对返回的数据进行处理

  • 背景
    • 1.项目层次

背景

最近公司有个需求是对返回数据进行处理,比如进行数据脱敏。最后在gateway中进行处理。

1.项目层次

根据项目的结构,原本在菜单功能处有对于权限设计的url判断,所以在url后面加了一个正则表达式的字段,例如“/^(1[3-9][0-9])\d{4}(\d{4}$)/:$1****$2”
因为正则表达式,我存储在redis和本地缓存中,表达式中的转义符号一定要注意,我在处理时,处理了转义,所以在页面填写时需要多加


```java
package com.qlisv.qqdznyyglpt.gateway.filter;import cn.hutool.json.JSONUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.qlisv.qqdznyyglpt.gateway.feign.PermissionsClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;/*** @author 63418*/
@Slf4j
@Component
public class DataDesensitizationResponseFilter implements GlobalFilter, Ordered {@Resourceprivate PermissionsClient permissionsClient;@Resourceprivate RedisTemplate redisTemplate;public static final String redisDataDesensitizationKey = "redisDataDesensitizationKey:%s:%s";public static Cache<String, Object> cache = CacheBuilder.newBuilder()// 初始容量.initialCapacity(5)// 最大缓存数,超出淘汰.maximumSize(50)// 过期时间 设置写入3秒后过期.expireAfterWrite(60, TimeUnit.SECONDS).build();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("DataDesensitizationResponseFilter---path={}", exchange.getRequest().getPath());if (exchange.getRequest().getMethod() == HttpMethod.OPTIONS) {return chain.filter(exchange);}boolean isExternalRequest = isExternalRequest(exchange);
//        boolean checkContentTypeAndUrl = checkContentTypeAndUrl(exchange);if (isExternalRequest) {// 如果是外部请求,处理返回的数据return processResponse(exchange, chain);}return chain.filter(exchange);}private boolean isExternalRequest(ServerWebExchange exchange) {// 判断请求是否来自外部// 可以根据实际情况进行自定义判断.这个判断基于nginx代理,如果后面有外部程序调用,根据场景修改此处判断,,nginx的配置文件一定要加上X-Nginx-Proxy这个headerServerHttpRequest request = exchange.getRequest();if (request.getHeaders().containsKey("X-Nginx-Proxy") &&request.getHeaders().containsKey("X-Real-IP")) {return true;}return false;}private Mono<Void> processResponse(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpResponse originalResponse = exchange.getResponse();DataBufferFactory bufferFactory = originalResponse.bufferFactory();try {ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {MediaType contentType = this.getHeaders().getContentType();String contentTypeString = contentType.toString();if (!contentTypeString.startsWith(MediaType.APPLICATION_JSON_VALUE) || !exchange.getResponse().getStatusCode().equals(HttpStatus.OK)) {return super.writeWith(body);}// 异步获取会话中的属性return exchange.getSession().flatMap(session -> {// 获取会话中的 username 和 tenantIdString username = session.getAttribute("username");String tenantId = session.getAttribute("tenantId");String userId = session.getAttribute("userId");// 创建一个Map,包含 username 和 tenantIdMap<String, String> name = new HashMap<>();name.put("username", username);name.put("tenantId", tenantId);name.put("userId", userId);// 返回这个Mapreturn Mono.just(name);}).flatMap(nameMap -> {Map<String, String> dataDesensitizationMap = new LinkedHashMap<>();try {dataDesensitizationMap = getDataDesensitizationMap(nameMap.get("username"), nameMap.get("userId"), nameMap.get("tenantId"));if (!checkDataDesensitizationMap(dataDesensitizationMap, exchange)) {return super.writeWith(body);}} catch (Exception e) {log.error("[DataDesensitizationResponseFilter] 获取配置和正则表达式异常", e);return super.writeWith(body);}if (body instanceof Flux) {Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;Map<String, String> finalDataDesensitizationMap = dataDesensitizationMap;return super.writeWith(fluxBody.buffer().map(dataBuffers -> {byte[] newContent = new byte[0];try {DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer join = dataBufferFactory.join(dataBuffers);byte[] content = new byte[join.readableByteCount()];join.read(content);DataBufferUtils.release(join);// 获取响应数据String responseStr = new String(content, StandardCharsets.UTF_8);// 获取响应数据List<String> strings = exchange.getResponse().getHeaders().get(HttpHeaders.CONTENT_ENCODING);if (!CollectionUtils.isEmpty(strings) && strings.contains("gzip")) {GZIPInputStream gzipInputStream = null;try {gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(content), content.length);StringWriter writer = new StringWriter();IOUtils.copy(gzipInputStream, writer, StandardCharsets.UTF_8);responseStr = writer.toString();} catch (IOException e) {log.error("====Gzip IO error", e);} finally {if (gzipInputStream != null) {try {gzipInputStream.close();} catch (IOException e) {log.error("===Gzip IO close error", e);}}}} else {responseStr = new String(content, StandardCharsets.UTF_8);}newContent = desensitizeJson(responseStr, finalDataDesensitizationMap).getBytes(StandardCharsets.UTF_8);originalResponse.getHeaders().setContentLength(newContent.length);} catch (Exception e) {log.error("responseStr exchange error", e);throw new RuntimeException(e);}return bufferFactory.wrap(newContent);}));}return super.writeWith(body);});}@Overridepublic Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {return writeWith(Flux.from(body).flatMapSequential(p -> p));}};return chain.filter(exchange.mutate().response(decoratedResponse).build());} catch (Exception e) {log.error("RewriteResponse error", e);return chain.filter(exchange);}}private static String desensitizeJson(String json, Map<String, String> dataDesensitization) {// 在这里实现你的 JSON 脱敏逻辑// 例如,使用 JSON 库解析 JSON,修改需要脱敏的字段,然后再序列化回 JSON 字符串// 返回脱敏后的 JSON 字符串String regularExpression = dataDesensitization.get("RegularExpression");String[] regularExpressionArr = regularExpression.split(";");for (String regularExpressionStr : regularExpressionArr) {String[] regularExpressionStrArr = regularExpressionStr.split(":");String regularExpressionStrKey = regularExpressionStrArr[0];String regularExpressionStrValue = regularExpressionStrArr[1];//这个地方的转义字符好坑,,,,Pattern pattern = Pattern.compile(StringEscapeUtils.unescapeJava(regularExpressionStrKey));Matcher matcher = pattern.matcher(json);json = matcher.replaceAll(StringEscapeUtils.unescapeJava(regularExpressionStrValue));}return json;}/*** 通过本地缓存或者redis缓存获取脱敏规则,规则的刷新在CustomServerAuthenticationSuccessHandler ,登录设置session时刷新* @param username* @param userId* @param tenantId* @return*/private Map<String, String> getDataDesensitizationMap(String username, String userId, String tenantId) {String key = String.format(redisDataDesensitizationKey, tenantId, userId);Map reDataDesensitization = null;try {reDataDesensitization = (Map) cache.get(key, () -> {if (redisTemplate.hasKey(key)) {Object dataDesensitization = redisTemplate.opsForValue().get(key);redisTemplate.expire(key, 60, TimeUnit.MINUTES);return dataDesensitization;}return null;});cache.put(key, reDataDesensitization);if (reDataDesensitization != null) {return reDataDesensitization;}} catch (Exception e) {log.error("[DataDesensitizationResponseFilter] 从缓存获取数据异常", e);}Map<String, String> dataDesensitization = permissionsClient.findDataDesensitizationByUsername(username, userId, tenantId);cache.put(key, dataDesensitization);redisTemplate.opsForValue().set(key, dataDesensitization, 60, TimeUnit.MINUTES);return dataDesensitization;}private Boolean checkDataDesensitizationMap(Map<String, String> dataDesensitization, ServerWebExchange exchange) {if (dataDesensitization == null) {return false;}if (!dataDesensitization.containsKey("data_desensitization")) {return false;}String data_desensitization = dataDesensitization.get("data_desensitization");if ("false".equals(data_desensitization)) {return false;}ServerHttpRequest request = exchange.getRequest();// 获取请求的路径String path = request.getPath().toString();for (String key : dataDesensitization.keySet()) {// 如果dataDesensitization Map中的某个键与请求的路径匹配,则返回true// spring的路径匹配工具,匹配一些特殊写法AntPathMatcher matcher = new AntPathMatcher();if (matcher.match(key, path)) {// 检查对应路径的值是否为true,如果是则表示需要进行数据脱敏dataDesensitization.put("RegularExpression", dataDesensitization.get(key));return true;}}return false;}@Overridepublic int getOrder() {return -2;}public static void main(String[] args) {String str="{\"customerContacts\":[],\"businessData\":[{\"name\":\"测试\",\"id\":\"ab7025db-e61e-4c4f-8dc9-8bf5539e05ad\",\"value\":[[\"ID\",\"姓名\",\"性别\",\"出生日期\",\"备注\",\"权限\"]]},{\"name\":\"预约\",\"id\":\"f3a99a5d-ec79-4523-a632-2dbc6f6bf83f\",\"value\":[[\"ID\",\"社保\",\"公积金\",\"数据\",\"其他\"],[\"87bccd32-7b48-4cb8-88f2-8eaaf7d15e65\",\"二档\",\"是\",\"2024-03-22 11:40:12\",\"李十四\"]]}],\"customerInfoExt\":{\"id\":null,\"tenantId\":null,\"customerId\":null,\"delStatus\":0,\"field1\":null,\"field2\":null,\"field3\":null,\"field4\":null,\"field5\":null,\"field6\":null,\"field7\":null,\"field8\":null,\"field9\":null,\"field10\":null,\"field11\":null,\"field12\":null,\"field13\":null,\"field14\":null,\"field15\":null,\"field16\":null,\"field17\":null,\"field18\":null,\"field19\":null,\"field20\":null,\"field21\":null,\"field22\":null,\"field23\":null,\"field24\":null,\"field25\":null,\"field26\":null,\"field27\":null,\"field28\":null,\"field29\":null,\"field30\":null,\"field31\":null,\"field32\":null,\"field33\":null,\"field34\":null,\"field35\":null,\"field36\":null,\"field37\":null,\"field38\":null,\"field39\":null,\"field40\":null,\"field41\":null,\"field42\":null,\"field43\":null,\"field44\":null,\"field45\":null,\"field46\":null,\"field47\":null,\"field48\":null,\"field49\":null,\"field50\":null},\"customer\":{\"id\":\"972d7770-0d1a-4c5b-a171-5ab4ff144764\",\"tenantId\":\"8654e94c-f430-4574-ab08-6b4bbbad1e9d\",\"customerName\":\"罗棉\",\"customerCode\":\"1\",\"sex\":0,\"age\":24,\"education\":4,\"birthday\":\"2024-03-22\",\"certificateType\":\"0\",\"certificateNo\":\"511***********1023\",\"maritalStatus\":1,\"accountLocation\":\"广东深圳龙华区民治大道2\",\"accountLocationPostal\":\"632200\",\"currentPostal\":\"456789\",\"address\":\"深圳市龙华区民治街道水围小区\",\"agentId\":\"1002\",\"accountManager\":\"李三\",\"email\":\"3570178990@qq.com\",\"qq\":\"12568900\",\"wechat\":\"ql8563515\",\"weiboNumber\":\"12345123\",\"personalTelephone\":\"17890804789\",\"personalMobilePhone\":\"17890890000\",\"officePhone\":\"13956789012\",\"homePhone\":\"12345678901\",\"emergencyContactPhone\":\"15908765439\",\"otherNumber1\":\"1234567890\",\"otherNumber2\":\"一档\",\"otherNumber3\":null,\"otherNumber4\":null,\"otherNumber5\":null,\"industry\":\"1\",\"industryType\":\"3\",\"companyNature\":\"1\",\"companyName\":\"飞牛公司\",\"companyPost\":\"委员\",\"companySize\":\"1\",\"companyAddress\":\"深圳市龙华区民治街道水围小区\",\"companyPostal\":\"636600\",\"companyPhone\":\"15267890562\",\"workingYears\":1.0,\"annualIncome\":null,\"socialSecurity\":1,\"delStatus\":0,\"creater\":\"jmh\",\"createTime\":\"2024-03-22 11:35:57\",\"updater\":\"jmh\",\"updateTime\":\"2024-03-25 17:15:04\",\"customerType\":1,\"customerBusinessId\":\"2010\",\"sourceFrom\":\"1\"}}";System.out.println(str.replaceAll("/^(1[3-9][0-9])\\d{4}(\\d{4}$)/","$1****$2"));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/6267.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CGAL 网格测地线距离计算

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 网格测地线距离是指在三维网格模型中计算两点之间的最短路径距离,考虑了网格的拓扑结构和几何形状。与传统的欧几里德距离不同,测地线距离考虑了网格的曲面形状,因此更适用于描述三维空间中的距离。 二、实现代码…

docker常用容器启动命令

docker常用容器启动命令 mysql启动redis启动nginx配置文件&启动 mysql启动 docker run -itd --name mysql-test --restartalways -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 mysqlredis启动 docker run -itd --name redis-test --restartalways -p 6379:6379 redisnginx…

蓝桥杯练习系统(算法训练)ALGO-949 勇士和地雷阵

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 勇士们不小心进入了敌人的地雷阵&#xff08;用n行n列的矩阵表示&#xff0c;*表示某个位置埋有地雷&#xff0c;-表示某个…

yolov5-pytorch-Ultralytics训练+预测+报错处理记录

一、前言 玩一段时间大模型&#xff0c;也该回归一下图像识别。本项目用于记录使用基于Ultralytics的yolov5进行目标检测测试。为什么用Ultralytics呢&#xff1f;答案有3 1、其良好的生态&#xff0c;方便我们部署到其它语言和设备上。因此本次测试结论&#xff1a;大坑没有&…

技术周总结 2024.04.29-05.05

一、python的数据表处理 """ 删除 Doris库中某些表中无效的数据 """ import mysql.connector import socket import socks import pandas as pd import pymysql from sqlalchemy import create_engine, text import csv from datetime import da…

UE5 蓝图入门

基础节点创建&#xff1a; 常量&#xff1a; 按住 1 &#xff0c;点击鼠标左键&#xff0c;创建常量 二维向量&#xff1a; 按住 2 &#xff0c;点击鼠标左键&#xff0c;创建二维向量 三维向量&#xff1a; 按住 3 &#xff0c;点击鼠标左键 乘法&#xff1a; 按住 m 键…

基于node.js+css+html+mysql博客系统

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、Php、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

【C++风云录】图形和动作捕捉库全面解析:从OpenPose到OpenCV

深入浅出&#xff1a;六大计算机视觉和动作捕捉库的教程和比较 前言 本文将为读者详细介绍六种在计算机视觉领域广泛使用的开源软件和SDK&#xff0c;包括OpenPose、Vicon SDK、Intel RealSense SDK、Microsoft Kinect SDK、PCL (Point Cloud Library)和OpenCV。我们会一一解…

Vue进阶之Vue项目实战(一)

Vue项目实战 项目搭建初始化eslint版本约束版本约束eslint配置 stylelintcspellcz-githusky给拦截举个例子 zx 项目搭建 node版本&#xff1a;20.11.1 pnpm版本&#xff1a;9.0.4 初始化 vue3最新的脚手架 pnpm create vite byelide-demo --template vue-ts pnpm i pnpm dev…

MIPS32 指令架构

指令格式 R 类型 说明&#xff1a; 用于寄存器和寄存器操作 参数说明: Op: 指令操作码Rs: 第一个源操作数寄存器号&#xff0c;参与运算使用Rd: 目的操作数寄存器号&#xff0c;保存结果使用Shamt: 位偏移量&#xff0c;仅在位移指令使用&#xff0c;在此直接置0Func: 指令函…

区块链 | IPFS:IPNS(入门版)

&#x1f98a;原文&#xff1a;IPFS 与 IPNS 啥关系&#xff1f; &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。这篇文章讲得太入门了&#xff0c;不涉及任何底层原理。 正文 随着 2019 年 12 月 12 日 Filecoin 测试网的上线&#xff0c;IPF…

深入 Django 模型层:数据库设计与 ORM 实践指南

title: 深入 Django 模型层&#xff1a;数据库设计与 ORM 实践指南 date: 2024/5/3 18:25:33 updated: 2024/5/3 18:25:33 categories: 后端开发 tags: Django ORM模型设计数据库关系性能优化数据安全查询操作模型继承 第一章&#xff1a;引言 Django是一个基于Python的开源…

安卓手机APP开发__媒体开发部分__分享声音的输入

安卓手机APP开发__媒体开发部分__分享声音的输入 目录 概述 安卓10之前的版本的行为 安卓10的行为 共享场景 小助手普通的APP 有可读取权的服务 普通的APP 两个普通的APP 语音电话 普通的APP 概述 声音的输入通常来自于内嵌的麦克风,还有外置的麦克网,或者是一个…

【C++】深入剖析C++11中右值引用和左值引用

目录 一、左值引用 && 右值引用 二、左值引用于右值引用的比较 三、 右值引用使用场景和意义 1、函数返回值 ①移动赋值 ②移动构造 2、STL容器插入接口 ​3、完美转发 一、左值引用 && 右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了…

【简单介绍下Lisp的学习历程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

计算机网络 3.1网络的拓扑结构

第三章 网络技术基础 第一节 网络的拓扑结构 一、认识拓扑结构 1.定义&#xff1a; ①网络节点和链路的几何图形&#xff1b; ②网络中网络单元的地理分布和互联关系的几何构型&#xff1b; ③通信子网的拓扑结构。 2.三要素&#xff1a;可靠性、扩充性、费用高低。 3.影…

react 子组件调用父组件方法,获取的数据不是最新值

目录 原因解决方法方法一、去掉 useCallback&#xff0c; 这样每次父组件触发刷新&#xff0c;就会刷新子组件方法二、或者通过监听 val&#xff0c;val 值改变来刷新函数方法三、在父组件中&#xff0c;把 val 作为 key 值&#xff0c;每次 val 变化强制触发更新 出现问题的代…

Mybatis之Sqlsession、Connection和Transaction三者间的关系

前言 最近在看Mybatis的源码&#xff0c;搜到这篇文章Sqlsession、Connection和Transaction原理与三者间的关系&#xff0c;debug之后发现有不少疑惑&#xff0c;于是按照原文整理了一下&#xff0c;记录下debug中的一些困惑点。 对于我们开发来讲&#xff0c;不管跟任何关系…

django搭建一个AI博客进行YouTube视频自动生成文字博客

文章目录 一、生成Django框架二、项目代码&#xff08;前端&#xff09;1、编写前端代码&#xff08;正文界面&#xff09;1.1、生产html框架1.2、添加live preview扩展1.3、更改title元素中文本1.4、添加CDN&#xff08;CSS&#xff09;样式链接1.5、nav标签1.6、在body标签中…

Python | Leetcode Python题解之第66题加一

题目&#xff1a; 题解&#xff1a; class Solution:def plusOne(self, digits: List[int]) -> List[int]:n len(digits)for i in range(n - 1, -1, -1):if digits[i] ! 9:digits[i] 1for j in range(i 1, n):digits[j] 0return digits# digits 中所有的元素均为 9retu…