前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud 网关(GlobalFilter)拦截响应体

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

原创
作者头像
江踏歌
修改2021-10-25 18:17:17
1.5K0
修改2021-10-25 18:17:17
举报

*需求场景:需要对返回的手机号等敏感信息进行脱敏处理*

用到: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);
    }
}

[银行卡工具类](https://blog.csdn.net/sinat_27403673/article/details/77340292)

[JsonPath节点使用文档](https://github.com/json-path/JsonPath)

[JsonPath中文文档](https://blog.csdn.net/londa/article/details/117534776)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档