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