4-服务网关Gateway_自定义过滤器学习笔记(2020.4.1)

4-服务网关Gateway_自定义过滤器学习笔记(2020.3.31)

前言:

Spring Cloud Gateway 已经内置了很多实用的过滤器,但并不能完全满足我们的需求。本文我们就来实现自定义过滤器。

用于常见的过滤请求, token验证, 请求参数验证什么的。

1.Filter 的生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。

G3yyGQ.png

“pre” 和 “post” 分别会在请求被执行前调用和被执行后调用,和 Zuul Filter 或 Spring Interceptor 中相关生命周期类似,但在形式上有些不一样。

Zuul 的 Filter 是通过 filterType() 方法来指定,一个 Filter 只能对应一种类型,要么是 “pre” 要么是 “post”。Spring Interceptor 是通过重写 HandlerInterceptor 中的三个方法来实现的。而 Spring Cloud Gateway 基于 Project Reactor 和 WebFlux,采用响应式编程风格,打开它的 Filter 的接口 GatewayFilter 你会发现它只有一个方法 filter

下面我们就来实现一下自定义过滤器, 对请求头的token进行认证。

自定义过滤器需要实现 GatewayFilterOrdered。其中 GatewayFilter 中的这个方法就是用来实现你的自定义的逻辑的

Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

Ordered 中的 int getOrder() 方法是来给过滤器设定优先级别的,值越大则优先级越低。

1.1 自定义普通过滤器

/**
 * 此过滤器功能为计算请求完成时间
 */
public class MyFilter implements GatewayFilter, Ordered {

    private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis());
        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);
                    if (startTime != null) {
                        System.out.println(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
                    }
                })
        );
    }

    /*
    *过滤器存在优先级,order越大,优先级越低
    */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

定义好MyFilter以后,其需要跟Route绑定使用,不能在application.yml文件中配置使用

@Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
         return builder.routes().route(r ->
                r.path("/abc/**")
                        //去掉前缀1节
                        .filters(p->p.stripPrefix(1))
                        //转发路由
                        .uri("lb://eureka-client")
                        //注册自定义过滤器
                        .filters(new MyFilter())
                        //给定id
                        .id("user-service")
         ).build();
    }

1.2 自定义全局过滤器

自定义过滤器, 假设对请求头的token进行认证。

/**
 * @Author: zhihao
 * @Date: 2020/3/31 17:38
 * @Description: 自定义过滤器
 * @Versions 1.0
 **/
@Component  //交给框架管理
public class MyGatewayFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //2.获取请求头Authenticate
        List<String> authenticate = request.getHeaders().get("Authenticate");
        if (null == authenticate || authenticate.isEmpty()) {
             //说明Authenticate为空,返回错误码,阻止通过网关路由
            ServerHttpResponse response = exchange.getResponse();
            Map<String,String> resultMap = new LinkedHashMap<>();
            resultMap.put("code", "401");
            resultMap.put("message", "没有token");
            String jsonStr = JSONUtil.toJsonStr(resultMap);
            //将错误信息输出页面
            DataBuffer dataBuffer = response.bufferFactory().wrap(jsonStr.getBytes());
            response.setStatusCode(HttpStatus.UNAUTHORIZED); //状态码
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            Mono<Void> mono = response.writeWith(Mono.just(dataBuffer));
            return mono;
            //return response.setComplete();
        } 
     		//......可以通过
            String[] strings = authenticate.toArray(new String[authenticate.size()]);
            String str = strings[0];
           
        
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        //值越大则优先级越低。
        return -1;
    }
 }

er(exchange);
}

@Override
public int getOrder() {
    //值越大则优先级越低。
    return -1;
}

}


1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章