返回介绍

Spring Cloud(七)服务网关 Zuul Filter 使用

发布于 2025-02-18 23:57:58 字数 9044 浏览 0 评论 0 收藏 0

上一篇文章中,讲了 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)

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

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

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

token is empty

password 测试

加上 tokenpassword &password=123456 ,已经验证通过

访问: http://127.0.0.1:9000/?token=token-uuid&password=123456

F5 刷新,每次都验证通过,并且负载均衡

The password cannot be empty

源码下载

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文