废话

可以先看看某大佬对Gateway过滤器的理解:GateWay过滤器 - 天宇轩-王 - 博客园

网关就像是一个电影院的门口,所有观众进场都需要从这个门口进去,大点的影院可能有多个门口(集群),所以一些验票操作、识别操作、统计操作等都需要在门口这里完成,并且还要高效,不然所有人都拥挤在门口,或者是进门1小时观影1分钟,那就得出事故了。除了验票之外,还要给工作人员(资源文件)这类无需验票的人员放行。

所以权限校验这里,能用在内存层面完成的就在内存解决,内存解决不了的就换缓存,缓存还解决不了的才去数据库寻求支持,数据库都解决不了的,杀个程序员祭天吧。

概述

Gateway过滤器主要分两种,GatewayFilter局部过滤器、GlobalFilter全局过滤器。

这里主要实现2个逻辑:

(1)LogFilter,用于记录输入输出日志

(2)AuthFilter,用于校验请求是否合法、用户权限校验等(不怕麻烦的话,用户权限校验可以再封装一个过滤器)

GlobalFilter主要就是两个方法,filter和getOrder,filter就是过滤逻辑执行的地方了,order则表示过滤器的等级,数值越低等级越优先。所以我们将LogFilter的等级设置为-1为最高级别,记录所有的请求和响应信息,AuthFilter则设置10。

需要注意的是LogFilter这里,因为是需要等接口处理完成之后,才能把返回的日志拉出来记录,这个东西相比纯过滤器,耗费的资源应该要多很多,如果有其他更好的办法记录日志的话,就尽量不要在这里记录了。

代码

配置啥的就省略了,详情参考上一个文章。

具体逻辑跟注意事项我都写代码里了,直接看吧:

@Slf4j
@Component//这个不能漏啊,不然扫描不到这里就会导致过滤器不生效
public class LogFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    //这里需要注意,如果是在本过滤器或者是其他过滤器中,过滤规则不通过然后直接返回错误信息的话,是到不了这一步的,这点切记。
                    //这个问题目前我没找到解决方案,所以建议其他过滤器那边,认证不通过的时候,另外也记录一份日志。
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
                        //从缓存中把返回的数据提取出来
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffer);
                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);

                        //释放掉内存
                        DataBufferUtils.release(join);
                        String s = new String(content, StandardCharsets.UTF_8);

                        //记录输入输出
                        log.info("type:api;param:{};cookie:{};hear:{};url:{};value:{}",
                                exchange.getRequest().getQueryParams().toString(), exchange.getRequest().getCookies().toString(),
                                exchange.getRequest().getHeaders().toString(), exchange.getRequest().getURI().toString(),
                                s);

                        //返回的结果已经提取出来打日志了,所以需要将数据重新写入
                        byte[] uppedContent = new String(s.getBytes(), StandardCharsets.UTF_8).getBytes();
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                return super.writeWith(body);
            }
        };
        //继续执行
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //权限验证逻辑,忽略,这里直接返回验证不通过
        if (true) {
            ServerHttpResponse response = exchange.getResponse();
            DataBuffer buffer = response.bufferFactory().wrap("非法登录".getBytes(StandardCharsets.UTF_8));
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            return response.writeWith(Mono.just(buffer));
        }
        //认证通过,往下走
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 10;
    }
}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐