1、前言
RSA:非对称加密技术,也可以作为验签的机制。公钥加密,私钥解密,由于私钥不传输,最为安全。
AES:对称加密技术,可逆,双方秘钥一致。
token:令牌验证技术,一般具有时效性。
MD5,SHA: 数据校验技术,一般生成数据的唯一标记值,不可逆,常用来验证数据完整性。
BASE64,URLEncoder:报文编码技术,可逆。
加签时,MD5,SHA等签名都是可逆的,所以如果没有其他机制,容易伪造。token和RSA则不容易伪造。
加密时,RSA最难破解;AES一方私钥泄露,另一方也泄露。BASE64,URLEncoder随时可破解,比明文安全一点
1.Gateway的拦截器
GlobalFilter:全局过滤拦截器
Ordered:拦截器的顺序
Gateway的核心接口:GatewayFilter,GlobalFilter,GatewayFilterChain
ServerWebExchange就相当于当前请求和响应的上下文。ServerWebExchange命名为服务网络交换器,存放着重要的请求-响应属性、请求实例和响应实例等等,有点像Context的角色
ServerHttpRequest实例是用于承载请求相关的属性和请求体,Spring Cloud Gateway中底层使用Netty处理网络请求
ServerHttpResponse实例是用于承载响应相关的属性和响应体,Spring Cloud Gateway中底层使用Netty处理网络请求。
Spring Cloud Gateway 的 Filter 的生命周期有两个:“pre” 和 “post”。
“pre”和 “post” 分别会在请求被执行前调用和被执行后调用,和 Zuul Filter 或 Spring Interceptor 中相关生命周期类似,但在形式上有些不一样。
Zuul 的 Filter 是通过filterType()方法来指定,一个 Filter 只能对应一种类型,要么是 “pre” 要么是“post”。Spring Interceptor 是通过重写HandlerInterceptor中的三个方法来实现的。而 Spring Cloud Gateway 基于 Project Reactor 和 WebFlux,采用响应式编程风格,打开它的 Filter 的接口GatewayFilter你会发现它只有一个方法filter。
GatewayFilterChain–网关过滤链表
GatewayFilter–网关路由过滤器
2、需求 :#
请求-> filter -> filter -> 路由业务处理 -> filter -> filter -> 返回
2.1、分析
request:加密请求–> 网关拦截:对数据进行解密验签 --> 解密的数据路由到相应的服务中 --> 服务返回数据 --> 网关拦截:对数据进行加密 --> response
3 实现需求
步骤
1、使用AES加密,对请求数据进行加密处理,生成sign签名字符串,请求服务器
2、gateway 全局过滤器拦截请求,对body进行缓存读取–>放行
3、拦截器1对请求进行再次拦截,读取body请求参数,获取到加密数据,对sign数据进行验签,若签名正确则解密数据,并重新封装好解密后的数据到request,分发路由到相应的服务当中,若验签失败,返回404错误
3、服务–>返回数据
4、拦截器2对返回数据进行拦截,并对数据进行AES加密处理,重新封装好response,返回给客户端
5、客户端拿到数据后解密
4、gateway拦截器使用
全局过滤器设置
不需要在配置文件配置,所有到服务的请求都会进行拦截,在类上加上@Configuration生效
指定过滤器设置
可在配置文件中配置,可以为某个服务指定过滤器。这里值得注意的是, 如果你filter的名称后缀是 GatewayFilterFactory, 如TestGatewayFilterFactory 在配置文件中只需要写Test即可 如果不是那就写全称就可以了,类上加入@Component
4.1 get请求
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("authToken");
if (token == null || token.isEmpty()) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
4.2 post请求
gateway采用了webflux的方式来封装的请求体
Flux<DataBuffer> body = exchange.getRequest().getBody();
body.subscribe(buffer -> {
byte[] bytes = new byte[buffer.readableByteCount()];
buffer.read(bytes);
DataBufferUtils.release(buffer);
try {
String bodyString = new String(bytes, "utf-8");
System.out.println(bodyString);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
});
这种方式只能获取一次body参数,其他过滤器想要再次获取会报错,获取之后需要再次封装好request请求到路由或者其他的过滤器中
解决body只能读取一次的问题,读取到后重新封装转发
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (exchange.getRequest().getHeaders().getContentType() == null) {
return chain.filter(exchange);
} else {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
DataBufferUtils.retain(dataBuffer);
//读取body 封装
Flux<DataBuffer> cachedFlux = Flux
.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
// 重新封装请求 其他过滤器能够访问到
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
}
}
解决无法修改请求体的问题
byte[] bytes = newBody.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer bodyDataBuffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
bodyDataBuffer.write(bytes);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
ServerHttpRequest buildReq = request.mutate().uri(request.getURI()).build();
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
int length = bytes.length;
// headers.remove(HttpHeaders.CONTENT_LENGTH);
// 修改请求体长度
headers.setContentLength(length);
headers.set(HttpHeaders.CONTENT_TYPE, "application/json");
// 重新封装好request
buildReq = new ServerHttpRequestDecorator(buildReq) {
@Override
public HttpHeaders getHeaders() {
return headers;
}
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
// 返回
return chain.filter(exchange.mutate().request(buildReq).build());