springboot getaway网关拦截加密验签方案

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());
"getaway zuul" 是一个网络流行语,原意是指一种通过转移注意力或脱身的方式来逃避困难或尴尬局面。这个短语来源于电影《捍卫者联盟》中一只名叫尤尔的角色,它具有很强的逃避能力。 在生活中,我们经常面临各种挑战和压力,有时候可能会感到束手无策或不知所措。这时候,采用getaway zuul的方式可能会给我们带来一些启示。 getaway zuul提醒我们要学会转移注意力和寻找解决问题的新角度。当我们陷入僵局时,可以试着暂时将焦点从问题上转移到其他事物上,例如参加一项爱好活动、与朋友聚会或者只是简单地放松自己。通过转移注意力,我们的思维可能会得到缓解,从而更容易找到解决问题的新方向。 此外,getaway zuul也提醒我们要有一种适时的“脱身”能力。当我们身陷困境或者面对尴尬场面时,有时候选择适当地离开或者改变环境可能会更加明智。这样可以减少冲突的升级或者尴尬的尺度,更有利于我们保持心态的平衡和保护自己的形象。 然而,getaway zuul并不意味着逃避责任或者逃避困难。在面对挑战时,我们要有勇气正视问题,并采取积极的行动来解决。getaway zuul更倾向于提醒我们要灵活应对情况,用更智慧的方式处理问题,而不是盲目地与问题对抗。 在人生的旅途中,getaway zuul 可以给我们带来新的思维角度,帮助我们更好地应对挑战与压力。我们需要学会寻找平衡点,既不被困扰,也不盲目逃避,而是用更明智的方式面对困难,获得更好的结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值