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());