公众号:踏歌的 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