`
hbxflihua
  • 浏览: 660235 次
  • 性别: 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
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics