`
hbxflihua
  • 浏览: 689789 次
  • 性别: 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网关-压测用.zip

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

    springcloud下通过gateway转发websocket

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

    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...

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

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

    详解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 相关的依赖以及可能的...

    springcloud-gateway获取应用响应信息乱码

    Spring Cloud Gateway 是 Spring Cloud 生态系统中的一个核心组件,主要提供 API 网关的功能。它作为系统的统一入口点,可以进行路由转发、过滤等操作,然而在处理应用响应信息时,可能会遇到乱码问题,这通常与字符...

    SpringCloud Finchley Gateway 缓存请求Body和Form表单的实现

    在构建微服务架构时,Spring Cloud Gateway作为API网关,起着至关重要的作用。它提供了路由、过滤器等功能,能够方便地对上游请求进行处理和转发。然而,有些场景下,我们可能需要缓存请求的Body(如JSON数据)或...

Global site tag (gtag.js) - Google Analytics