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登录验证码校验,使用randomstr参数作为每次生成验证码图片的唯一标识,验证码Kaptcha插件
Spring Cloud Gateway的全局异常处理 Spring Cloud Gateway中的全局异常处理不能直接用@ControllerAdvice来处理,通过跟踪异常信息的抛出,找到对应的源码,自定义一些处理逻辑来符合业务的需求。 网关都是给接口做...
springboot spring aop 拦截器 注解方式实现脱敏(涉及到:pom.xml -->application.properties --->启动类-->拦截器)
Spring Cloud Gateway 版本为 3.1.3 大家好,欢迎来到阿提说说博客 “纸上得来终觉浅,绝知此事要躬行。”(南宋)陆游 目前相关的教程虽然有很多,但总觉得被各位大佬压缩,看到的只是一部分知识,并不全面,...
Spring官方博客发布了一篇关于Spring Cloud Gateway的CVE报告,据公告描述,当启用和暴露 Gateway Actuator 端点时,使用 Spring Cloud Gateway 的应用程序可受到代码注入攻击。攻击者可以发送特制的恶意请求,从而...
赠送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依赖信息文件:...
1.本项目为SpringCloud Gateway的微服务框架,整合了SpringSecurity,微服务间使用Redis来获取登陆的用户信息。 2.由于Gat
springcloud gateway 全局滤器 统一签名判定
SpringGateway集成Oauth2进行认证授权;还可以这么玩;SSO功能的认证;自己测试正常;SpringGateway集成Oauth2进行认证授权;还可以这么玩;SSO功能的认证;自己测试正常;SpringGateway集成Oauth2进行认证授权;还...
赠送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依赖信息文件:...
自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回...
websocket,springcloud
1.本项目为SpringCloud Gateway的微服务框架,整合了SpringSecurity,微服务间使用Redis来获取登陆的用户信息。 2.由于Gateway采用的是纯Webflux方式,所以原有的Spring基于传统拦截器、过滤器的方式无法正常使用...
spring cloud gateway的负载均衡和动态路由的实现 demo_01,demo_02,demo_03 这三个服务相当于是集群的微服务 gateway这个服务是 springcloude gateway + ribbon 做的负载均衡 gateway_01 这个服务 是动态路由的...
在接入Spring-Cloud-Gateway时,可能有需求进行缓存Json-Body数据或者Form-Urlencoded数据的情况。这篇文章主要介绍了SpringCloud Finchley Gateway 缓存请求Body和Form表单的实现,感兴趣的小伙伴们可以参考一下
这个项目提供了一个构建在 Spring 生态系统之上的 API 网关,包括:Spring 5,Spring ... Spring Cloud Gateway 旨在提供一种简单而有效的 API 路 由方式,并为其提供横切关注点,例如:安全,监控/指标和弹性。
赠送jar包:spring-cloud-gateway-server-3.0.4.jar; 赠送原API文档:spring-cloud-gateway-server-3.0.4-javadoc.jar; 赠送源代码:spring-cloud-gateway-server-3.0.4-sources.jar; 赠送Maven依赖信息文件:...
内容:完整集成了Spring Gateway, Spring Security, Nacos, OAuth2, RBAC, 手机验证码登录等SSO统一认证平台,全平台唯一,全平台最全,全面细致,已经帮你踩了很多坑,一看就会,可以本地运行,或者直接作为认证...
spingcloud gateway简单实例源码,spring-cloud-gateway-sample
普通javaweb项目调用springCloud接口(超级实用,很详细)但是要注意的事一定要对springboot和springcloud的有所了解,不然的话你也是看不懂的,大家一起努力共同学习