Spring Cloud 网关(GlobalFilter)拦截响应体

公众号:踏歌的 Java Daily

需求场景:需要对返回的手机号等敏感信息进行脱敏处理
用到:Hutool,JsonPath

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import lombok.extern.slf4j.Slf4j;
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.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Slf4j
@Component
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
    Configuration conf = Configuration.builder()
            .options(Option.AS_PATH_LIST).build();

    @Override
    public int getOrder() {
        return FilterOrderEnum.GATEWAY_CONTEXT_FILTER.getOrder();
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return chain.filter(exchange.mutate().response(responseDecorator(exchange)).build());
    }

    private ServerHttpResponseDecorator responseDecorator(ServerWebExchange exchange) {
        return new ServerHttpResponseDecorator(exchange.getResponse()) {
            ServerHttpResponse serverHttpResponse = exchange.getResponse();
            DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();

            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                return super.writeWith(DataBufferUtils.join(Flux.from(body))
                        .map(dataBuffer -> {
                            // 获取响应体
                            byte[] content = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(content);
                            DataBufferUtils.release(dataBuffer);
                            return content;
                        }).flatMap(bytes -> {
                            // 对响应体进行业务判断(返回值是OK,格式为JSON)
                            if (exchange.getResponse().getStatusCode().equals(HttpStatus.OK)
                                    && exchange.getResponse().getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0).equals(MediaType.APPLICATION_JSON_UTF8.toString())) {
                                // 将响应体转换为String
                                String bodyString = new String(uncompress(bytes), StandardCharsets.UTF_8);
                                log.info("bodyString: {}", bodyString);
                                // 进行业务处理
                                // TODO 调用业务处理方法
                                // 读取配置文件内容
                                List<String> filterField = null;
                                String dataHandling = dataHandling(responseData, filterField);
                                log.info("dataHandling: {}", dataHandling);
                                // 最后将返回的数据类型转换为byte
                                bytes = dataHandling.getBytes();
                            } else {

                            }
                            return Mono.just(bufferFactory.wrap(bytes));
                        }));
            }

            @Override
            public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
                return writeWith(Flux.from(body).flatMapSequential(p -> p));
            }
        };
    }

    public static byte[] uncompress(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e) {
            log.error("gzip uncompress error.", e);
        }

        return out.toByteArray();
    }

    /*编码 gzip*/
    public static byte[] compress(String str, String encoding) {
        if (str == null || str.length() == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(str.getBytes(encoding));
            gzip.close();
        } catch (IOException e) {
            log.error("gzip compress error.", e);
        }
        return out.toByteArray();

    }

    public String dataHandling(String responseData, List<String> filterField) {
        try {
            String content = responseData;
            for (String filterFieldS : filterField) {
                //获取所有的节点
                List<String> pathList = JsonPath.using(conf).parse(responseData).read("$.." + filterFieldS);
                for (String path : pathList) {
                    //获取父节点
                    String parentPath = StrUtil.removeSuffix(path, "['" + filterFieldS + "']");
                    //读取值
                    Object json = JsonPath.parse(content).read(path);
                    if (json != null) {
                        String read = json.toString();
                        //脱敏
                        String mobileMask = mask(read);
                        String keyName = filterFieldS + "Mask";
                        //父节点下添加元素
                        content = JsonPath.parse(content).put(parentPath, keyName, mobileMask).jsonString();
                    }
                }
            }
            return content;
        } catch (Exception e) {
            log.error(responseData, e);
        }
        return null;
    }

    public static String mask(String val) {
        if(StrUtil.isBlank(val)){
            return val;
        }
        if(val.length() < 3){
            return StrUtil.hide(val,1,val.length());
        }
        if(val.length() < 4){
            return StrUtil.hide(val,1,val.length()-1);
        }
        if(PhoneUtil.isMobile(val) || PhoneUtil.isMobileHk(val) || PhoneUtil.isMobileTw(val)){
            return StrUtil.desensitized(val,DesensitizedUtil.DesensitizedType.MOBILE_PHONE);
        }
        if(EnumUtil.isEnum(val)){
            return StrUtil.desensitized(val,DesensitizedUtil.DesensitizedType.EMAIL);
        }
        //TODO 银行卡的工具类hutool我没找到可以自行在网上查找
        return StrUtil.desensitized(val, DesensitizedUtil.DesensitizedType.ID_CARD);
    }
}


银行卡工具类
JsonPath节点使用文档
JsonPath中文文档

创作不易请多多支持,更多详情请关注wx工作号:xvguo1127

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Spring Cloud Gateway的全局拦截,你可以通过实现GlobalFilter接口来实现。GlobalFilterSpring Cloud Gateway提供的全局过滤器接口,可以在请求进入网关之前或者响应离开网关之前进行一些统一的处理。 你可以创建一个类实现GlobalFilter接口,并在类上添加@Component注解,将其注册为一个Spring Bean。在这个类中,你可以实现pre和post方法来定义请求进入和响应离开网关时的处理逻辑。 在pre方法中,你可以获取到请求的信息,可以对请求进行一些校验、修改或者记录日志等操作。在post方法中,你可以获取到响应的信息,可以对响应进行一些处理,比如修改响应内容、设置响应头等。 以下是一个简单的示例: ```java import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 在pre方法中进行请求处理逻辑 // ... return chain.filter(exchange).then(Mono.fromRunnable(() -> { // 在post方法中进行响应处理逻辑 // ... })); } @Override public int getOrder() { // 设置过滤器的执行顺序 return Ordered.H

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值