[Spring Cloud] - Zuul 實踐(三) - 過濾器

在設計系統階段,必定記住Zuul的核心思想:Zuul是作服務的路由,而不是頁面的轉發。這是zuul與Nginx根本上的不一樣。前端

Zuul沒有提供良好的跳轉或轉發功能,這和它的使用場景是有關的。它的核心功能包括驗證服務權限,驗證成功或失敗,成功或失敗後放行仍是攔截等等。若攔截,只需返回響應碼便可,至於成功或失敗的頁面顯示,則應該由前端負責跳轉或轉發。
請必定遵照標準化的設計,不要讓Zuul參與頁面跳轉(固然,Zuul也能夠實現頁面跳轉,本文後半部分會涉及到,但不推薦由Zuul實現跳轉)網絡

Zuul的驗證功能基本在它的過濾器中實現。ide

過濾器類型與請求的生命週期

每個請求發送到Zuul,都有其對應的不一樣階段,能夠稱其爲「生命週期」,好比初始化階段,處理請求階段,結束階段,出error階段等等。微服務

在談下一話題前,請必定分清楚「轉發」和「路由」的區別。
若是說定義,路由是一種策略,轉發是一條路徑。
說淺顯一點,路由是網狀結構下的各類路徑解的集合,而轉發是點對點的一條通訊路徑。
再淺顯一些,就像學生年代你上課時給女神小紅遞紙條,路由是你將紙條給你同桌(代理),你同桌會選擇一條消息發送的路徑,好比將紙條傳遞給A,再到B,再到C,最後到小紅。由同桌發到小紅的整個過程就叫路由。你只須要將紙條給同桌(代理),剩下的不須要你參與,你的同桌會選擇最優路徑,不管是經過ABC,仍是BAC,當紙條傳遞成功,你的代理會反饋給你結果(同桌會告訴你紙條傳遞成功,小紅看都沒看就扔進垃圾桶),你不須要知道中間隔了多少人。這就是路由。
而轉發,就是你直接把紙條揉成團,扔給小紅,小紅收到後含羞一笑,你看到後心花盛開。這就是轉發。
最淺顯的歸納就是: 路由是多跳的轉發,轉發的一跳的路由。這一點從英文routing和forwading的區別就能夠明白。
若是還沒法理解,請從新讀一遍計算機網絡。工具


對於微服務項目來講,Zuul實現的就是路由功能,每每也被會稱作路由轉發、網關等等,但意思都是同樣的。
本文在談Zuul的攔截器,
每個request在發送到Zuul後,都有幾個不一樣的階段(生命週期),Zuul的幾個攔截器對應request的不一樣階段,因此咱們先談一下request的生命週期:post

  1. 請求剛剛被Zuul接收到,Zuul須要解析請求的地址,以及其請求參數等等。
  2. 請求即將被路由。Zuul已經肯定此請求該發送至哪個service,並已準備好將其發送過去。
  3. 請求被路由至目標服務
  4. 請求在以上三個階段中的某一個出錯。

以上四個階段是request的生命週期,Zuul的四種攔截器分別對應以上的四個request生命週期: PRE, ROUTING, POST, ERROR.計算機網絡

因此,若是想要對request的某個生命週期進行操做(過濾),只須要按需實現這四種攔截器便可。設計

那麼,如何實現這四種攔截器呢?依然超級簡單,答案只有一個:繼承ZuulFilter類並覆寫父類方法便可。
區分攔截器的類型在方法filterType()中。
閒言少敘,上代碼。
如下是一個最基本的PRE攔截器。代理

實現攔截器

定義
新建一個類,繼承ZuulFilter,並覆寫其方法。code

public class PreRequestFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(RouteRequestFilter.class);

    // 表示此攔截器類型, pre、route、post、error
    @Override
    public String filterType() {
        return "pre";
    }
    
    // 執行順序,如有多個同種攔截器,好比有多個pre攔截器,他們的執行順序是什麼
    @Override
    public int filterOrder() {
        return 0;
    }

    // 是否應用這個攔截器,true是應用,false表示這個攔截器是失效的
    @Override
    public boolean shouldFilter() {
        return false;
    }

    // 此方法爲核心方法,表示攔截器中執行的邏輯
    @Override
    public Object run() throws ZuulException {
        System.out.println("pre");
        return null;
    }
}

註冊爲Bean
在主類中將此攔截器註冊爲Bean

@EnableZuulProxy
@SpringBootApplication
public class ZuulApp {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class, args);
    }

    @Bean
    public PreRequestLogFilter preRequestLogFilter(){
        return new PreRequestLogFilter();
    }

此時,一個最基本的攔截器就建立成功了!

攔截請求

在run方法中,添加如下內容:

@Override
    public Object run(){
        //獲取上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //獲取Request
        HttpServletRequest request = ctx.getRequest();
        //獲取請求參數accessToken
        String accessToken = request.getParameter("accessToken");
        //使用String工具類
        if (StringUtils.isBlank(accessToken)) {
            logger.error("no token");
            logger.warn("accessToken is empty");
            ctx.setSendZuulResponse(false);  //進行攔截
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("accessToken is empty");
            } catch (IOException e) {
                e.printStackTrace();
            }

            return null;
        }
        return null;
   }

此時,若經過Zuul訪問微服務,若沒有accessToken參數,會發現請求被攔截,頁面上會顯示「accessToken is empty」

相關文章
相關標籤/搜索