天天看點

SpringCloud服務網關-zuul過濾器

我們了解了 Spring Cloud Zuul 作為網關所具備的最基本功能:路由(Router),下面我們将關注 Spring Cloud Zuul 的另一核心功能:過濾器(Filter)

1.Filter的使用場景

場景非常多:

請求鑒權:一般放在pre類型,如果發現沒有通路權限,直接就攔截了
異常處理:一般會在error類型和post類型過濾器中結合來處理。
服務調用時長統計:pre和post結合使用。      

3.ZuulFilter

ZuulFilter是過濾器的頂級父類。

SpringCloud服務網關-zuul過濾器

在這裡我們看一下其中定義的4個最重要的方法:

public abstract ZuulFilter implements IZuulFilter{

    abstract public String filterType();

    abstract public int filterOrder();
    
    boolean shouldFilter();// 來自IZuulFilter

    Object run() throws ZuulException;// IZuulFilter
}      
  • ​shouldFilter​

    ​​:傳回一個​

    ​Boolean​

    ​值,判斷該過濾器是否需要執行。傳回true執行,傳回false不執行。
  • ​run​

    ​:過濾器的具體業務邏輯。
  • ​filterType​

    ​:傳回字元串,代表過濾器的類型。包含以下4種:
  • ​pre​

    ​:請求在被路由之前執行
  • ​routing​

    ​:在路由請求時調用
  • ​post​

    ​:在routing和errror過濾器之後調用
  • ​error​

    ​:處理請求時發生錯誤調用
  • ​filterOrder​

    ​:通過傳回的int值來定義過濾器的執行順序,數字越小優先級越高。

3.Filter 的生命周期

這張是Zuul官網提供的請求生命周期圖,清晰的表現了一個請求在各個過濾器的執行順序。

Filter 的生命周期有 4 個,分别是 “PRE”、“ROUTING”、“POST” 和“ERROR”,整個生命周期可以用下圖來表示

SpringCloud服務網關-zuul過濾器

Zuul 大部分功能都是通過過濾器來實作的,這些過濾器類型對應于請求的典型生命周期。

PRE:這種過濾器在請求被路由之前調用。我們可利用這種過濾器實作身份驗證、在叢集中選擇請求的微服務、記錄調試資訊等。
ROUTING:這種過濾器将請求路由到微服務。這種過濾器用于建構發送給微服務的請求,并使用 Apache HttpClient 或 Netfilx Ribbon 請求微服務。
POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應添加标準的 HTTP Header、收集統計資訊和名額、将響應從微服務發送給用戶端等。
ERROR:在其他階段發生錯誤時執行該過濾器。 除了預設的過濾器類型,Zuul 還允許我們建立自定義的過濾器類型。例如,我們可以定制一種 STATIC 類型的過濾器,直接在 Zuul 中生成響應,而不将請求轉發到後端的微服務。      
  • 正常流程:
  • 請求到達首先會經過pre類型過濾器,而後到達routing類型,進行路由,請求就到達真正的服務提供者,執行請求,傳回結果後,會到達post過濾器。而後傳回響應。
  • 異常流程:
  • 整個過程中,pre或者routing過濾器出現異常,都會直接進入error過濾器,再error處理完畢後,會将請求交給POST過濾器,最後傳回給使用者。
  • 如果是error過濾器自己出現異常,最終也會進入POST過濾器,而後傳回。
  • 如果是POST過濾器出現異常,會跳轉到error過濾器,但是與pre和routing不同的時,請求不會再到達POST過濾器了。

所有内置過濾器清單:

SpringCloud服務網關-zuul過濾器

Zuul中預設實作的Filter執行順序:

SpringCloud服務網關-zuul過濾器

如何禁用指定的Filter

可以在 application.yml 中配置需要禁用的 filter,格式為

zuul...disable=true。

比如要禁用​

​org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter​

​就設定

zuul:
  SendResponseFilter:
    post:
      disable: true      

自定義Filter:

首先自定義一個 Filter,繼承 ZuulFilter 抽象類,在 run() 方法中添加具體業務邏輯,具體如下:

/**
 * @author bruceliu
 * @create 2019-10-19 12:12
 * @description
 */
@Component
public class MyFilter extends ZuulFilter {

    /**
     * 過濾器的類型,它決定過濾器在請求的哪個生命周期中執行。 這裡定義為pre,代表會在請求被路由之前執行。
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * filter執行順序,通過數字指定。 數字越大,優先級越低。
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 判斷該過濾器是否需要被執行。這裡我們直接傳回了true,是以該過濾器對所有請求都會生效。 實際運用中我們可以利用該函數來指定過濾器的有效範圍。
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 過濾器的具體邏輯
     *
     * @return
     */
    @Override
    public Object run() {
        // 登入校驗邏輯。
        // 1)擷取Zuul提供的請求上下文對象
        RequestContext ctx = RequestContext.getCurrentContext();
        // 2) 從上下文中擷取request對象
        HttpServletRequest req = ctx.getRequest();
        // 3) 從請求中擷取token
        String token = req.getParameter("access-token");
        // 4) 判斷
        if(token == null || "".equals(token.trim())){
            // 沒有token,登入校驗失敗,攔截
            ctx.setSendZuulResponse(false);
            // 傳回401狀态碼。也可以考慮重定向到登入頁。
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        // 校驗通過,可以考慮把使用者資訊放入上下文,繼續向後執行
        return null;
    }
}      

在上面實作的過濾器代碼中,我們通過繼承ZuulFilter抽象類并重寫了下面的四個方法來實作自定義的過濾器。這四個方法分别定義了:

filterType():過濾器的類型,它決定過濾器在請求的哪個生命周期中執行。這裡定義為pre,代表會在請求被路由之前執行。
filterOrder():過濾器的執行順序。當請求在一個階段中存在多個過濾器時,需要根據該方法傳回的值來依次執行。通過數字指定,數字越大,優先級越低。
shouldFilter():判斷該過濾器是否需要被執行。這裡我們直接傳回了true,是以該過濾器對所有請求都會生效。實際運用中我們可以利用該函數來指定過濾器的有效範圍。
run():過濾器的具體邏輯。這裡我們通過ctx.setSendZuulResponse(false)令 Zuul 過濾該請求,不對其進行路由,然後通過ctx.setResponseStatusCode(401)設定了其傳回的錯誤碼,當然我們也可以進一步優化我們的傳回,比如,通過ctx.setResponseBody(body)對傳回 body 内容進行編輯等。      

繼續閱讀