1、材料
jdk 1.8 + springboot 2.7.13 + springcloud 2021.0.8 + fastjson2.0 + hutool
pom:
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-gateway</artifactid>
<version>3.1.8</version>
2、编写filter:
import cn.hutool.core.collection.collutil;
import com.alibaba.fastjson2.json;
import lombok.extern.slf4j.slf4j;
import org.reactivestreams.publisher;
import org.springframework.boot.autoconfigure.condition.conditionalonproperty;
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.core.io.buffer.defaultdatabufferfactory;
import org.springframework.http.httpstatus;
import org.springframework.http.mediatype;
import org.springframework.http.server.pathcontainer;
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.web.server.serverwebexchange;
import org.springframework.web.util.pattern.pathpatternparser;
import reactor.core.publisher.flux;
import reactor.core.publisher.mono;
import javax.annotation.resource;
import java.net.uri;
import java.nio.charset.standardcharsets;
import java.util.list;
import java.util.map;
import java.util.objects;
/**
* 定制密码过滤器
* <p>
* 1、请求解密
* 2、响应加密
* <p>
* 目前优先做 2
*/
@component
// 触发配置
// @conditionalonproperty(value = "security.responseencrypt", havingvalue = "true")
@slf4j
public class responseencryptfilter implements globalfilter, ordered {
@override
public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
// 获取请求体
serverhttprequest request = exchange.getrequest();
mediatype mediatype = exchange.getrequest().getheaders().getcontenttype();
// 判定是否为json请求
if (mediatype != null
&& mediatype.includes(mediatype.application_json) ) {
// 根据具体业务内容,修改响应体
return modifyresponsebody(exchange, chain);
}
return chain.filter(exchange);
}
@override
public int getorder() {
// 参看这个类 org.springframework.core.ordered; 值越小,优先级越高
return -200;
}
/**
* 修改响应体
*
* @param exchange
* @param chain
* @return
*/
private mono<void> modifyresponsebody(serverwebexchange exchange, gatewayfilterchain chain) {
serverhttpresponse originalresponse = exchange.getresponse();
originalresponse.getheaders().setcontenttype(mediatype.application_json);
databufferfactory bufferfactory = originalresponse.bufferfactory();
serverhttpresponsedecorator response = buildresponse(originalresponse, bufferfactory);
return chain.filter(exchange.mutate().response(response).build());
}
private serverhttpresponsedecorator buildresponse(serverhttpresponse originalresponse, databufferfactory bufferfactory) {
return new serverhttpresponsedecorator(originalresponse) {
@override
public mono<void> writewith(publisher<? extends databuffer> body) {
if (getstatuscode().equals(httpstatus.ok) && body instanceof flux) {
flux<? extends databuffer> fluxbody = flux.from(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 responsedata = new string(content, standardcharsets.utf_8);
//加密数据
responsedata = encryptresponse(responsedata);
byte[] uppedcontent = responsedata.getbytes(standardcharsets.utf_8);
originalresponse.getheaders().setcontentlength(uppedcontent.length);
return bufferfactory.wrap(uppedcontent);
}));
} else {
log.error("获取响应体数据 :" + getstatuscode());
}
return super.writewith(body);
}
@override
public mono<void> writeandflushwith(publisher<? extends publisher<? extends databuffer>> body) {
return writewith(flux.from(body).flatmapsequential(p -> p));
}
};
}
private string encryptresponse(string originalresponsebody) {
if (!secureproperties.enableencryptresponseparam()) {
log.info("响应结果加密,跳过");
return originalresponsebody;
}
map map = json.parseobject(originalresponsebody);
//处理返回的数据
object data = map.get("data");
if (data != null) {
// 加密
map.put("data",xxxx.encrypt(data.tostring()));
}
return json.tojsonstring(map);
}
}
3、加密算法
完整代码仓库:springcloud-gateway-feng-demo: 使用网关做请求和响应的加解密
springcloud gateway实现请求解密和响应加密_springcloudgateway filter进行请求跟返回参数加解密-csdn博客
4、不生效问题
一般情况是,filter order 的问题。 因为你这个filter执行的优先级比较低导致被截断
请看这里,比较详细
【spring cloud gateway】serverhttpresponsedecorator不生效-csdn博客
5、url匹配
比如我们只想对某些uri进行处理,处理方式如下:
// 调用
// 获取请求体
serverhttprequest request = exchange.getrequest();
isneedencrypt(request.geturi())
private boolean isneedencrypt(uri uri) {
list<string> encrypturilist = "你配置的待加密的uri列表";
log.info("encrypturilist:{}",encrypturilist);
if(collutil.isempty(encrypturilist)){
return false;
}
string path = uri.getpath();
for (string pathpattern : encrypturilist) {
if (ismatch(path, pathpattern)) {
return true;
}
}
return false;
}
private boolean ismatch(string path, string pathpattern) {
// 这里需要根据whitelisted的格式来判断和匹配
// 对于简单ip可以直接比较,对于范围和cidr需要额外逻辑
// ...
return pathpatternparser.defaultinstance.parse(pathpattern).matches(pathcontainer.parsepath(path));
}
6、小结
gateway处理加解密,比在其他地方处理更合理,当然,也可以通过requestbodyadvice 和 responsebodyadvice进行加解密。还有aop的方式 等等
发表评论