package com.huatech.gateway.filter; import cn.hutool.http.ContentType; import cn.hutool.http.Header; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import com.google.common.base.Charsets; import com. huatech.gateway.utils.RequestUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.reactivestreams.Publisher; import org.springframework.beans.factory.annotation.Value; 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.http.HttpMethod; 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.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Iterator; import java.util.Objects; /** * @author lihua_java@163.com * * <p> * 返回内容脱敏过滤器,将指定的字段脱敏 * </p> */ @Component @Slf4j public class ResponseDesensitizeFilter implements WebFilter, Ordered { @Value("${gateway.desensitize.switch:false}") private boolean desensitizeSwitch; @Value("${gateway.desensitize.ignoreUrls:}") private String[] ignoreUrls; @Value("${gateway.desensitize.fields:}") private String[] fields; private static final char DESENSITIZE_CHAR = '*'; /** * 返回值处理 * * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest originalRequest = exchange.getRequest(); log.debug("path : {}", originalRequest.getURI().getPath()); if(!desensitizeSwitch){ return chain.filter(exchange); } if(fields == null || fields.length == 0){ return chain.filter(exchange); } if (originalRequest.getMethod() != HttpMethod.POST) { return chain.filter(exchange); } if(RequestUtils.isXhr(originalRequest) || RequestUtils.isMiniProgram(originalRequest)){ log.debug("X-Requested-With : {}", originalRequest.getHeaders().getFirst(RequestUtils.HEADER_KEY_X_REQUEST_WITH)); return chain.filter(exchange); } String path = originalRequest.getURI().getPath(); if(ignoreUrlsMatch(path)){ log.debug("ignoreUri : {}", path); return chain.filter(exchange); } ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux) { if (ContentType.JSON.toString() .equals(originalResponse.getHeaders().getFirst(Header.CONTENT_TYPE.toString()))) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>)body; return super.writeWith(fluxBody.buffer().map(dataBuffers -> { DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer join = dataBufferFactory.join(dataBuffers); byte[] content = new byte[join.readableByteCount()]; join.read(content); // 释放掉内存 DataBufferUtils.release(join); String responseString = new String(content, Charsets.UTF_8); if (StringUtils.isNotBlank(responseString)) { JSONObject respJson = JSONObject.parseObject(responseString); String data = respJson.getString("data"); if(JSONUtil.isJsonArray(data)){// 返回集合 JSONArray items = JSONObject.parseArray(data); desensitizeJsonArray(items); respJson.put("data", items); }else if(JSONUtil.isJsonObj(data)){// 返回单个对象 JSONObject item = JSONObject.parseObject(data); if(item.containsKey("records")){// 分页 JSONArray items = item.getJSONArray("records"); desensitizeJsonArray(items); }else{ desensitizeJson(item); } respJson.put("data", item); } responseString = JSONObject.toJSONString(respJson, SerializerFeature.MapSortField); } byte[] uppedContent = responseString.getBytes(Charsets.UTF_8); return bufferFactory.wrap(uppedContent); })); } } return super.writeWith(body); } }; // replace response with decorator return chain.filter(exchange.mutate().response(decoratedResponse).build()); } private void desensitizeJsonArray(JSONArray items){ if(Objects.isNull(items) || items.isEmpty()){ return; } Iterator iterator = items.stream().iterator(); while (iterator.hasNext()){ JSONObject item = (JSONObject) iterator.next(); desensitizeJson(item); } } /** * 字符串脱敏 * @param item */ private void desensitizeJson(JSONObject item){ if(Objects.isNull(item) || item.isEmpty()){ return; } //log.debug("before desensitize, item : {}", item.toJSONString()); for(String fieldKey :fields){ Object field = item.get(fieldKey); if(Objects.nonNull(field) && field instanceof String){ String fieldVal = field.toString(); int len = fieldVal.length(); if(len <= 1){ continue; } int start = (len == 2) ? 1 : (len / 3); int end = (len == 2) ? 1 : ((len - 1) - start); fieldVal = replace(fieldVal, start, end, DESENSITIZE_CHAR); item.put(fieldKey, fieldVal); } } //log.debug("after desensitize, item : {}", item.toJSONString()); } /** * 字符串替换 * @param value * @param start * @param end * @param var * @return */ private static String replace(String value, int start, int end, char var){ if(StringUtils.isBlank(value)){ return value; } if(end >= value.length()){ end = value.length() - 1; } if(start > end || start >= value.length()){ return value; } StringBuilder sb = new StringBuilder(value.length()); char[] vs = value.toCharArray(); for(int i = 0, j = vs.length; i < j; i++){ if(i < start || i > end){ sb.append(vs[i]); }else if(i >= start && i <= end){ sb.append(var); } } return sb.toString(); } private AntPathMatcher antPathMatcher = new AntPathMatcher(); private Boolean ignoreUrlsMatch(String path){ for(String pattern : ignoreUrls){ if(antPathMatcher.match(pattern, path)){ log.debug("path match, pattern : {}, path : {}", pattern, path); return true; } } return false; } @Override public int getOrder() { return 101; } }
import org.springframework.http.server.reactive.ServerHttpRequest; /** * @author lihua_java@163.com * @version 2.2.0 * @date 2023/10/19 */ public class RequestUtils { public static final String HEADER_KEY_X_REQUEST_WITH = "X-Requested-With"; public static final String HEADER_VAL_MINI_PROGRAM_REQUEST = "MiniProgramRequest"; public static final String HEADER_VAL_XML_HTTP_REQUEST = "XMLHttpRequest"; public static boolean isXhr(ServerHttpRequest request) { return HEADER_VAL_XML_HTTP_REQUEST.equalsIgnoreCase(request.getHeaders().getFirst(HEADER_KEY_X_REQUEST_WITH)); } public static boolean isMiniProgram(ServerHttpRequest request) { return HEADER_VAL_MINI_PROGRAM_REQUEST.equalsIgnoreCase(request.getHeaders().getFirst(HEADER_KEY_X_REQUEST_WITH)); } }
相关推荐
Spring Gateway 是一个基于 Spring Framework 5、Project Reactor 和 Spring Cloud 的强大网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。在本项目中,我们将探讨如何在 Spring Gateway 中...
2. **Predicate(谓词)**:源自Java 8的函数接口,用于断言输入(通常是Spring Framework的ServerWebExchange)是否满足条件。 3. **Filter(过滤器)**:允许自定义操作,用于在请求转发到下游服务之前或之后修改...
内容:完整集成了Spring Gateway, Spring Security, Nacos, OAuth2, RBAC, 手机验证码登录等SSO统一认证平台,全平台唯一,全平台最全,全面细致,已经帮你踩了很多坑,一看就会,可以本地运行,或者直接作为认证...
在构建分布式系统时,Spring Cloud Gateway 作为微服务架构中的边缘服务或 API 网关,扮演着至关重要的角色。它负责路由请求到相应的微服务,并可以提供过滤器功能,如限流、熔断等。而Spring Security 则是 Java ...
在IT行业中,Spring Cloud Gateway作为Spring Cloud生态体系中的一个关键组件,被广泛用于构建微服务架构中的API网关。这个框架允许我们集中处理各种请求,包括路由、过滤、安全等,极大地简化了服务间的通信。而...
SpringCloud Gateway作为一款现代化的微服务网关,它在企业级分布式系统中扮演着至关重要的角色。这个名为"springcloud Gateway网关-压测用.zip"的压缩包包含了一个用于性能测试的配置,目的是评估和优化Gateway的...
在Spring Cloud Gateway中,全局过滤器(Global Filter)是一种强大的机制,用于在请求路由到具体的服务之前或之后执行通用的处理逻辑。在这个场景中,我们关注的是如何利用全局过滤器来实现统一的签名验证,这在...
Spring官方博客发布了一篇关于Spring Cloud Gateway的CVE报告,据公告描述,当启用和暴露 Gateway Actuator 端点时,使用 Spring Cloud Gateway 的应用程序可受到代码注入攻击。攻击者可以发送特制的恶意请求,从而...
总结一下,通过上述步骤,我们已经在Spring Boot应用中利用Spring AOP和注解方式实现了数据脱敏。这个拦截器可以在不修改原有业务代码的情况下,确保敏感信息在响应给客户端之前得到处理,提高了应用的安全性。同时...
赠送jar包:spring-cloud-gateway-server-3.1.1.jar; 赠送原API文档:spring-cloud-gateway-server-3.1.1-javadoc.jar; 赠送源代码:spring-cloud-gateway-server-3.1.1-sources.jar; 赠送Maven依赖信息文件:...
### Spring Cloud Gateway全局异常处理详解 #### 一、引言 在微服务架构中,网关作为服务入口,承担着路由转发、限流熔断、鉴权认证等职责。Spring Cloud Gateway作为一款基于Spring Framework 5、Project Reactor...
ErrorWebExceptionHandler 接口是 SpringCloud Gateway 提供的异常处理接口,我们可以通过实现这个接口来处理系统级异常。DefaultErrorWebExceptionHandler 是 SpringCloud Gateway 提供的默认异常处理实现,我们...
1.本项目为SpringCloud Gateway的微服务框架,整合了SpringSecurity,微服务间使用Redis来获取登陆的用户信息。 2.由于Gateway采用的是纯Webflux方式,所以原有的Spring基于传统拦截器、过滤器的方式无法正常使用...
这个项目提供了一个构建在 Spring 生态系统之上的 API 网关,包括:Spring 5,Spring ... Spring Cloud Gateway 旨在提供一种简单而有效的 API 路 由方式,并为其提供横切关注点,例如:安全,监控/指标和弹性。
赠送jar包:spring-cloud-gateway-server-3.1.1.jar; 赠送原API文档:spring-cloud-gateway-server-3.1.1-javadoc.jar; 赠送源代码:spring-cloud-gateway-server-3.1.1-sources.jar; 赠送Maven依赖信息文件:...
在Spring Cloud生态体系中,Spring Cloud Gateway作为新一代的API网关,被广泛应用于微服务架构中,用于统一处理请求路由、过滤器链、限流、熔断等核心功能。本篇将详细介绍Spring Cloud Gateway的配置文件相关知识...
3. **自定义过滤器**:如果需要定制化的过滤器逻辑,可以在项目中编写自定义的 Filter 类,实现 Spring Cloud Gateway 所需的接口。 4. **POM 文件**:依赖管理,包含了 Spring Cloud Gateway 相关的依赖以及可能的...
自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回...
Spring Cloud Gateway 配置 Hystrix 熔断、限流、后台调用注意点 Spring Cloud Gateway 是一种基于 Spring Boot 框架的 API 网关解决方案,提供了许多实用的功能来管理和保护微服务架构中的 API。其中,Hystrix ...
spring cloud gateway的负载均衡和动态路由的实现 demo_01,demo_02,demo_03 这三个服务相当于是集群的微服务 gateway这个服务是 springcloude gateway + ribbon 做的负载均衡 gateway_01 这个服务 是动态路由的...