Spring Cloud(七)服務(wù)網(wǎng)關(guān) Zuul Filter 使用

2019-04-17 13:59 更新

上一篇文章中,講了Zuul 轉(zhuǎn)發(fā),動(dòng)態(tài)路由,負(fù)載均衡,等等一些Zuul 的特性,這個(gè)一篇文章,講Zuul Filter 使用,關(guān)于網(wǎng)關(guān)的作用,這里就不再次贅述了,重點(diǎn)是zuul的Filter ,我們可以實(shí)現(xiàn)安全控制,比如,只有請(qǐng)求參數(shù)中有token和密碼的客戶端才能訪問服務(wù)端的資源。那么如何來實(shí)現(xiàn)Filter了?

Spring Cloud Zuul

zuul 執(zhí)行流程

執(zhí)行流程圖

Zuul大部分功能都是通過過濾器來實(shí)現(xiàn)的。Zuul中定義了四種標(biāo)準(zhǔn)過濾器類型,這些過濾器類型對(duì)應(yīng)于請(qǐng)求的典型生命周期。

PRE:這種過濾器在請(qǐng)求被路由之前調(diào)用。我們可利用這種過濾器實(shí)現(xiàn)身份驗(yàn)證、在集群中選擇請(qǐng)求的微服務(wù)、記錄調(diào)試信息等。

ROUTING:這種過濾器將請(qǐng)求路由到微服務(wù)。這種過濾器用于構(gòu)建發(fā)送給微服務(wù)的請(qǐng)求,并使用Apache HttpClient或Netfilx Ribbon請(qǐng)求微服務(wù)。

OST:這種過濾器在路由到微服務(wù)以后執(zhí)行。這種過濾器可用來為響應(yīng)添加標(biāo)準(zhǔn)的HTTP Header、收集統(tǒng)計(jì)信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端等。

ERROR:在其他階段發(fā)生錯(cuò)誤時(shí)執(zhí)行該過濾器。

除了默認(rèn)的過濾器類型,Zuul還允許我們創(chuàng)建自定義的過濾器類型。例如,我們可以定制一種STATIC類型的過濾器,直接在Zuul中生成響應(yīng),而不將請(qǐng)求轉(zhuǎn)發(fā)到后端的微服務(wù)。

準(zhǔn)備工作

我們先拿之前兩篇文章,構(gòu)建的兩個(gè)微服務(wù)代碼為基礎(chǔ),進(jìn)行下面的操作

建議先閱讀以下兩篇文章

Spring Cloud(四) 服務(wù)提供者 Eureka + 服務(wù)消費(fèi)者 Feign
Spring Cloud(三) 服務(wù)提供者 Eureka + 服務(wù)消費(fèi)者(rest + Ribbon)

http://www.ymq.io/2017/12/06/spring-cloud-feign/

http://www.ymq.io/2017/12/05/spring-cloud-ribbon-rest/

Eureka Service

導(dǎo)入第三篇文章中的項(xiàng)目:作為服務(wù)注冊(cè)中心

spring-cloud-eureka-service

Eureka Provider

導(dǎo)入第三篇文章中的項(xiàng)目:作為服務(wù)的提供者

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

簡單使用

新建項(xiàng)目 spring-cloud-zuul-filter

添加依賴

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

開啟服務(wù)注冊(cè)

在程序的啟動(dòng)類 ZuulFilterApplication 通過 @EnableZuulProxy 開啟 Zuul 服務(wù)網(wǎng)關(guān)

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中核心組件,通過繼承該抽象類,覆寫幾個(gè)關(guān)鍵方法達(dá)到自定義調(diào)度請(qǐng)求的作用

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"; // 可以在請(qǐng)求被路由之前調(diào)用
    }


    @Override
    public int filterOrder() {
        return 0; // filter執(zhí)行順序,通過數(shù)字指定 ,優(yōu)先級(jí)為0,數(shù)字越大,優(yōu)先級(jí)越低
    }


    @Override
    public boolean shouldFilter() {
        return true;// 是否執(zhí)行該過濾器,此處為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");// 獲取請(qǐng)求的參數(shù)


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


}

PasswordFilter

ZuulFilter 是Zuul中核心組件,通過繼承該抽象類,覆寫幾個(gè)關(guān)鍵方法達(dá)到自定義調(diào)度請(qǐng)求的作用

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"; // 請(qǐng)求處理完成后執(zhí)行的filter
    }


    @Override
    public int filterOrder() {
        return 1; // 優(yōu)先級(jí)為0,數(shù)字越大,優(yōu)先級(jí)越低
    }


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


    @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;
        }
    }
}

開啟過濾器

在程序的啟動(dòng)類 ZuulFilterApplication 添加 Bean

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


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

filterType

filterType:返回一個(gè)字符串代表過濾器的類型,在zuul中定義了四種不同生命周期的過濾器類型,具體如下:

  • pre:路由之前
  • routing:路由之時(shí)
  • post: 路由之后
  • error:發(fā)送錯(cuò)誤調(diào)用
  • filterOrder:過濾的順序
  • shouldFilter:這里可以寫邏輯判斷,是否要過濾,本文true,永遠(yuǎn)過濾。
  • run:過濾器的具體邏輯??捎煤軓?fù)雜,包括查sql,nosql去判斷該請(qǐng)求到底有沒有權(quán)限訪問。

測試服務(wù)

依次啟動(dòng)項(xiàng)目:

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

啟動(dòng)該工程后,訪問服務(wù)注冊(cè)中心,查看服務(wù)是否都已注冊(cè)成功:http://localhost:8761/

查看各個(gè)服務(wù)注冊(cè)狀態(tài)

查看 eureka 監(jiān)控,看服務(wù)是否都注冊(cè)成功

token 測試

訪問:http://127.0.0.1:8761/

步驟一 提示 token is empty

訪問:http://127.0.0.1:9000/

瀏覽器訪問

步驟二 加上token ?token=token-uuid ,已經(jīng)驗(yàn)證通過了,提示 The password cannot be empty

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

token is empty

password 測試

![The password cannot be empty][3]

加上tokenpassword &password=123456 ,已經(jīng)驗(yàn)證通過

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

F5 刷新,每次都驗(yàn)證通過,并且負(fù)載均衡

Hello Zuul

                

源碼下載

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.souyunku.com
  • Email:admin@souyunku.com
  • 版權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明出處
  • Wechat:關(guān)注公眾號(hào),搜云庫,專注于開發(fā)技術(shù)的研究與知識(shí)分享

    關(guān)注公眾號(hào)-搜云庫

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)