`
hbxflihua
  • 浏览: 683215 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring Gateway 接口返回值脱敏

阅读更多
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));
    }
}

 

0
0
分享到:
评论

相关推荐

    spring-gateway实现登录验证码校验的代码,百分百可用

    Spring Gateway 是一个基于 Spring Framework 5、Project Reactor 和 Spring Cloud 的强大网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。在本项目中,我们将探讨如何在 Spring Gateway 中...

    最新Spring Cloud Gateway 官方参考指南-中文版-3.x

    2. **Predicate(谓词)**:源自Java 8的函数接口,用于断言输入(通常是Spring Framework的ServerWebExchange)是否满足条件。 3. **Filter(过滤器)**:允许自定义操作,用于在请求转发到下游服务之前或之后修改...

    Spring Gateway+Security+OAuth2+RBAC 实现SSO统一认证平台

    内容:完整集成了Spring Gateway, Spring Security, Nacos, OAuth2, RBAC, 手机验证码登录等SSO统一认证平台,全平台唯一,全平台最全,全面细致,已经帮你踩了很多坑,一看就会,可以本地运行,或者直接作为认证...

    Spring Cloud Gateway 整合 Spring Security 统一登录认证鉴权

    在构建分布式系统时,Spring Cloud Gateway 作为微服务架构中的边缘服务或 API 网关,扮演着至关重要的角色。它负责路由请求到相应的微服务,并可以提供过滤器功能,如限流、熔断等。而Spring Security 则是 Java ...

    springcloud下通过gateway转发websocket

    在IT行业中,Spring Cloud Gateway作为Spring Cloud生态体系中的一个关键组件,被广泛用于构建微服务架构中的API网关。这个框架允许我们集中处理各种请求,包括路由、过滤、安全等,极大地简化了服务间的通信。而...

    springcloud Gateway网关-压测用.zip

    SpringCloud Gateway作为一款现代化的微服务网关,它在企业级分布式系统中扮演着至关重要的角色。这个名为"springcloud Gateway网关-压测用.zip"的压缩包包含了一个用于性能测试的配置,目的是评估和优化Gateway的...

    springcloud gateway 全局过滤器统一签名判定.doc

    在Spring Cloud Gateway中,全局过滤器(Global Filter)是一种强大的机制,用于在请求路由到具体的服务之前或之后执行通用的处理逻辑。在这个场景中,我们关注的是如何利用全局过滤器来实现统一的签名验证,这在...

    CVE-2022-22947-Spring-Cloud-Gateway 内存马POC

    Spring官方博客发布了一篇关于Spring Cloud Gateway的CVE报告,据公告描述,当启用和暴露 Gateway Actuator 端点时,使用 Spring Cloud Gateway 的应用程序可受到代码注入攻击。攻击者可以发送特制的恶意请求,从而...

    springboot spring aop 拦截器注解方式实现脱敏

    总结一下,通过上述步骤,我们已经在Spring Boot应用中利用Spring AOP和注解方式实现了数据脱敏。这个拦截器可以在不修改原有业务代码的情况下,确保敏感信息在响应给客户端之前得到处理,提高了应用的安全性。同时...

    spring-cloud-gateway-server-3.1.1-API文档-中文版.zip

    赠送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 Cloud Gateway作为一款基于Spring Framework 5、Project Reactor...

    详解SpringCloud Finchley Gateway 统一异常处理

    ErrorWebExceptionHandler 接口是 SpringCloud Gateway 提供的异常处理接口,我们可以通过实现这个接口来处理系统级异常。DefaultErrorWebExceptionHandler 是 SpringCloud Gateway 提供的默认异常处理实现,我们...

    spring_gateway_security_webflux.rar

    1.本项目为SpringCloud Gateway的微服务框架,整合了SpringSecurity,微服务间使用Redis来获取登陆的用户信息。 2.由于Gateway采用的是纯Webflux方式,所以原有的Spring基于传统拦截器、过滤器的方式无法正常使用...

    Spring Cloud Gateway 2.1 使用手册中文版.pdf

    这个项目提供了一个构建在 Spring 生态系统之上的 API 网关,包括:Spring 5,Spring ... Spring Cloud Gateway 旨在提供一种简单而有效的 API 路 由方式,并为其提供横切关注点,例如:安全,监控/指标和弹性。

    spring-cloud-gateway-server-3.1.1-API文档-中英对照版.zip

    赠送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依赖信息文件:...

    SpringCloud.03.网关Gateway 配置文件

    在Spring Cloud生态体系中,Spring Cloud Gateway作为新一代的API网关,被广泛应用于微服务架构中,用于统一处理请求路由、过滤器链、限流、熔断等核心功能。本篇将详细介绍Spring Cloud Gateway的配置文件相关知识...

    springcloud gateway

    3. **自定义过滤器**:如果需要定制化的过滤器逻辑,可以在项目中编写自定义的 Filter 类,实现 Spring Cloud Gateway 所需的接口。 4. **POM 文件**:依赖管理,包含了 Spring Cloud Gateway 相关的依赖以及可能的...

    自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏

    自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回数据进行脱敏 自定义注解结合Hutool对SpringBoot接口返回...

    spring cloud gateway配置Hystrix 熔断、限流、后台调用注意点.pdf

    Spring Cloud Gateway 配置 Hystrix 熔断、限流、后台调用注意点 Spring Cloud Gateway 是一种基于 Spring Boot 框架的 API 网关解决方案,提供了许多实用的功能来管理和保护微服务架构中的 API。其中,Hystrix ...

    spring_cloud_gateway负载均衡,动态路由

    spring cloud gateway的负载均衡和动态路由的实现 demo_01,demo_02,demo_03 这三个服务相当于是集群的微服务 gateway这个服务是 springcloude gateway + ribbon 做的负载均衡 gateway_01 这个服务 是动态路由的...

Global site tag (gtag.js) - Google Analytics