1. 程式人生 > 其它 >Spring Cloud(七)服務閘道器 Zuul Filter 使用

Spring Cloud(七)服務閘道器 Zuul Filter 使用

上一篇文章中,講了Zuul 轉發,動態路由,負載均衡,等等一些Zuul 的特性,這個一篇文章,講Zuul Filter 使用,關於閘道器的作用,這裡就不再次贅述了,重點是zuul的Filter ,我們可以實現安全控制,比如,只有請求引數中有token和密碼的客戶端才能訪問服務端的資源。那麼如何來實現Filter了?

Spring Cloud Zuul

zuul 執行流程

Zuul大部分功能都是通過過濾器來實現的。Zuul中定義了四種標準過濾器型別,這些過濾器型別對應於請求的典型生命週期。

PRE:這種過濾器在請求被路由之前呼叫。我們可利用這種過濾器實現身份驗證、在叢集中選擇請求的微服務、記錄除錯資訊等。

ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建傳送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。

OST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應新增標準的HTTP Header、收集統計資訊和指標、將響應從微服務傳送給客戶端等。

ERROR:在其他階段發生錯誤時執行該過濾器。

除了預設的過濾器型別,Zuul還允許我們建立自定義的過濾器型別。例如,我們可以定製一種STATIC型別的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。

準備工作

我們先拿之前兩篇文章,構建的兩個微服務程式碼為基礎,進行下面的操作

建議先閱讀以下兩篇文章

Spring Cloud(四) 服務提供者 Eureka + 服務消費者 Feign Spring Cloud(三) 服務提供者 Eureka + 服務消費者(rest + Ribbon)

Eureka Service

匯入第三篇文章中的專案:作為服務註冊中心

spring-cloud-eureka-service

Eureka Provider

匯入第三篇文章中的專案:作為服務的提供者

spring-cloud-eureka-provider-1 spring-cloud-eureka-provider-2 spring-cloud-eureka-provider-3

簡單使用

新建專案 spring-cloud-zuul-filter

新增依賴

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>

開啟服務註冊

在程式的啟動類 ZuulFilterApplication 通過 @EnableZuulProxy 開啟 Zuul 服務閘道器

package io.ymq.example.zuul.filter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

@EnableZuulProxy
@SpringBootApplication
public class ZuulFilterApplication {

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

新增配置

配置檔案 application.yml

spring:
  application:
    name: zuul-service-filter

server:
  port: 9000

zuul:
  routes:
    api:
        path: /**
        serviceId: eureka-provider

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

TokenFilter

ZuulFilter 是Zuul中核心元件,通過繼承該抽象類,覆寫幾個關鍵方法達到自定義排程請求的作用

TokenFilter 過濾器

package io.ymq.example.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

/**
 * 描述: 過濾器 token
 *
 * @author yanpenglei
 * @create 2017-12-11 14:38
 **/
public class TokenFilter extends ZuulFilter {

    private final Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);

    @Override
    public String filterType() {
        return "pre"; // 可以在請求被路由之前呼叫
    }

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

    @Override
    public boolean shouldFilter() {
        return true;// 是否執行該過濾器,此處為true,說明需要過濾
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        LOGGER.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        String token = request.getParameter("token");// 獲取請求的引數

        if (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true); //對請求進行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
            return null;
        } else {
            ctx.setSendZuulResponse(false); //不對其進行路由
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess", false);
            return null;
        }
    }

}

PasswordFilter

ZuulFilter 是Zuul中核心元件,通過繼承該抽象類,覆寫幾個關鍵方法達到自定義排程請求的作用

PasswordFilter 過濾器

package io.ymq.example.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

/**
 * 描述: 過濾器 Password
 *
 * @author yanpenglei
 * @create 2017-12-11 15:40
 **/
public class PasswordFilter extends ZuulFilter {

    private final Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);

    @Override
    public String filterType() {
        return "post"; // 請求處理完成後執行的filter
    }

    @Override
    public int filterOrder() {
        return 1; // 優先順序為0,數字越大,優先順序越低
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        return (boolean) ctx.get("isSuccess");
        // 判斷上一個過濾器結果為true,否則就不走下面過濾器,直接跳過後面的所有過濾器並返回 上一個過濾器不通過的結果。
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        LOGGER.info("--->>> PasswordFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        String username = request.getParameter("password");
        if (null != username && username.equals("123456")) {
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
            return null;
        } else {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("The password cannot be empty");
            ctx.set("isSuccess", false);
            return null;
        }
    }
}

開啟過濾器

在程式的啟動類 ZuulFilterApplication 新增 Bean

@Bean
public TokenFilter tokenFilter() {
	return new TokenFilter();
}

@Bean
public PasswordFilter PasswordFilter() {
	return new PasswordFilter();
}

filterType

filterType:返回一個字串代表過濾器的型別,在zuul中定義了四種不同生命週期的過濾器型別,具體如下:

  • pre:路由之前
  • routing:路由之時
  • post: 路由之後
  • error:傳送錯誤呼叫
  • filterOrder:過濾的順序
  • shouldFilter:這裡可以寫邏輯判斷,是否要過濾,本文true,永遠過濾。
  • run:過濾器的具體邏輯。可用很複雜,包括查sql,nosql去判斷該請求到底有沒有許可權訪問。

測試服務

依次啟動專案:

spring-cloud-eureka-service spring-cloud-eureka-provider-1 spring-cloud-eureka-provider-2 spring-cloud-eureka-provider-3 spring-cloud-zuul-filter

啟動該工程後,訪問服務註冊中心,檢視服務是否都已註冊成功:http://localhost:8761/

檢視 eureka 監控,看服務是否都註冊成功

token 測試

訪問:http://127.0.0.1:8761/

步驟一 提示 token is empty

訪問:http://127.0.0.1:9000/

步驟二 加上token ?token=token-uuid ,已經驗證通過了,提示 The password cannot be empty

訪問:http://127.0.0.1:9000/?token=token-uuid

password 測試

加上tokenpassword &password=123456 ,已經驗證通過

訪問:http://127.0.0.1:9000/?token=token-uuid&password=123456

F5 重新整理,每次都驗證通過,並且負載均衡

原始碼下載

GitHub:https://github.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul-filter

碼雲:https://gitee.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul-filter

Contact

  • 作者:鵬磊
  • 出處:http://www.ymq.io
  • Email:[email protected]
  • 版權歸作者所有,轉載請註明出處
  • Wechat:關注公眾號,搜雲庫,專注於開發技術的研究與知識分享