上篇文章中我们了解了 Spring Cloud Zuul 作为网关所具备的最基本功能:路由(Router),下面我们将关注 Spring Cloud Zuul 的另一核心功能:过滤器(Filter)。

Filter 的作用

我们已经能够实现请求的路由功能,所以我们的微服务应用提供的接口就可以通过统一的 API 网关入口被客户端访问到了。
但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都需要有一定的限制,系统并不会将所有的微服务接口都对它们开放。然而,目前的服务路由并没有限制权限这样的功能,所有请求都会被毫无保留地转发到具体的应用并返回结果。
为了实现对客户端请求的安全校验和权限控制,最简单和粗暴的方法就是为每个微服务应用都实现一套用于校验签名和鉴别权限的过滤器或拦截器。不过,这样的做法并不可取,它会增加日后的系统维护难度,因为同一个系统中的各种校验逻辑很多情况下都是大致相同或类似的,这样的实现方式会使得相似的校验逻辑代码被分散到了各个微服务中去,冗余代码的出现是我们不希望看到的。所以,比较好的做法是将这些校验逻辑剥离出去,构建出一个独立的鉴权服务。在完成了剥离之后,有不少开发者会直接在微服务应用中通过调用鉴权服务来实现校验,但是这样的做法仅仅只是解决了鉴权逻辑的分离,并没有在本质上将这部分不属于业余的逻辑拆分出原有的微服务应用,冗余的拦截器或过滤器依然会存在。

对于这样的问题,更好的做法是通过前置的网关服务来完成这些非业务性质的校验。由于网关服务的加入,外部客户端访问我们的系统已经有了统一入口,既然这些校验与具体业务无关,那何不在请求到达的时候就完成校验和过滤,而不是转发后再过滤而导致更长的请求延迟。同时,通过在网关中完成校验和过滤,微服务应用端就可以去除各种复杂的过滤器和拦截器了,这使得微服务应用的接口开发和测试复杂度也得到了相应的降低。

Filter 的生命周期

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

Zuul 大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。

  • PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul 还允许我们创建自定义的过滤器类型。例如,我们可以定制一种 STATIC 类型的过滤器,直接在 Zuul 中生成响应,而不将请求转发到后端的微服务。

Zuul 中默认实现的 Filter

类型 顺序 过滤器 功能
pre -3 ServletDetectionFilter 标记处理 Servlet 的类型
pre -2 Servlet30WrapperFilter 包装 HttpServletRequest 请求
pre -1 FormBodyWrapperFilter 包装请求体
route 1 DebugFilter 标记调试标志
route 5 PreDecorationFilter 处理请求上下文供后续使用
route 10 RibbonRoutingFilter serviceId 请求转发
route 100 SimpleHostRoutingFilter url 请求转发
route 500 SendForwardFilter forward 请求转发
post 0 SendErrorFilter 处理有错误的请求响应
post 1000 SendResponseFilter 处理正常的请求响应

如何禁用指定的 Filter

可以在 application.yml 中配置需要禁用的 filter,格式为zuul.<SimpleClassName>.<filterType>.disable=true
比如要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter就设置

zuul:
SendResponseFilter:
post:
disable: true

自定义 Filter

首先自定义一个 Filter,继承 ZuulFilter 抽象类,在 run() 方法中添加具体业务逻辑,具体如下:

 package com.carry.springcloud;

 import javax.servlet.http.HttpServletRequest;

 import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext; 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() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest(); String token = request.getParameter("token");
if (token == null || token.isEmpty()) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("token is empty");
}
return null;
}
}

在上面实现的过滤器代码中,我们通过继承ZuulFilter抽象类并重写了下面的四个方法来实现自定义的过滤器。这四个方法分别定义了:

  • filterType():过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为pre,代表会在请求被路由之前执行。
  • filterOrder():过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行。通过数字指定,数字越大,优先级越低。
  • shouldFilter():判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的有效范围。
  • run():过滤器的具体逻辑。这里我们通过ctx.setSendZuulResponse(false)令 Zuul 过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回 body 内容进行编辑等。

在实现了自定义过滤器之后,它并不会直接生效,我们还需要为其创建具体的 Bean 才能启动该过滤器,比如,在应用主类中增加如下内容:

 package com.carry.springcloud;

 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 ServiceApiGatewayApplication { public static void main(String[] args) {
SpringApplication.run(ServiceApiGatewayApplication.class, args);
} @Bean
public MyFilter myFilter() {
return new MyFilter();
}
}

重新启动service-api-gateway,并发起下面的请求,对上面定义的过滤器做一个验证:

Spring Cloud学习笔记【八】服务网关 Zuul(过滤器)的更多相关文章

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

    上一篇文章中,讲了Zuul 转发,动态路由,负载均衡,等等一些Zuul 的特性,这个一篇文章,讲Zuul Filter 使用,关于网关的作用,这里就不再次赘述了,重点是zuul的Filter ,我们可 ...

  2. Spring Cloud(六)服务网关 zuul 快速入门

    服务网关是微服务架构中一个不可或缺的部分.通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由.均衡负载功能之外,它还具备了权限控制等功能.Spring Cloud Netflix中 ...

  3. Spring Cloud 系列之 Gateway 服务网关(四)

    本篇文章为系列文章,未读第一集的同学请猛戳这里: Spring Cloud 系列之 Gateway 服务网关(一) Spring Cloud 系列之 Gateway 服务网关(二) Spring Cl ...

  4. Spring Cloud 系列之 Gateway 服务网关(二)

    本篇文章为系列文章,未读第一集的同学请猛戳这里:Spring Cloud 系列之 Gateway 服务网关(一) 本篇文章讲解 Gateway 网关的多种路由规则.动态路由规则(配合服务发现的路由规则 ...

  5. Spring Cloud 系列之 Gateway 服务网关(三)

    本篇文章为系列文章,未读第一集的同学请猛戳这里: Spring Cloud 系列之 Gateway 服务网关(一) Spring Cloud 系列之 Gateway 服务网关(二) 本篇文章讲解 Ga ...

  6. Spring Cloud 系列之 Gateway 服务网关(一)

    什么是 Spring Cloud Gateway Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由 ...

  7. Spring Cloud学习笔记-009

    API网关服务:Spring Cloud Zuul API网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的Façade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户 ...

  8. Spring Cloud学习笔记-005

    服务消费者 之前已经搭建好了微服务中的核心组件——服务注册中心(包括单节点模式和高可用模式).也有了服务提供者,接下来搭建一个服务消费者,它主要完成两个目标,发现服务以及消费服务.其中,服务发现的任务 ...

  9. Spring Cloud 学习笔记(一)——入门、特征、配置

    [TOC] 0 放在前面 0.1 参考文档 http://cloud.spring.io/spring-cloud-static/Brixton.SR7/ https://springcloud.cc ...

随机推荐

  1. Android学习——LinearLayout布局实现居中、左对齐、右对齐

    android:orientation="vertical"表示该布局下的元素垂直排列: 在整体垂直排列的基础上想要实现内部水平排列,则在整体LinearLayout布局下再创建一 ...

  2. Enable .Net 4.5 in IIS on Windows 8.1

    Setting up a new development box for myself I had forgotten all about the necessity to use theaspnet ...

  3. ActiveMQ学习笔记(10)----ActiveMQ容错的连接

    1. Failover Protocol 前面讲述的都是Client配置连接到指定的broker上,但是,如果Broker的连接失败怎么办呢?此时,Client有两个选项:要么立刻死掉,要么连接到其他 ...

  4. Python3基础笔记---线程与进程

    参考博客:Py西游攻关之多线程(threading模块) 一.并发与并行的区别 并发:交替做不同事的能力并行:同时做不同事的能力 行话解释:并发:不同代码块交替执行的性能并行:不同代码块同时执行的性能 ...

  5. Map的5种遍历方式

    public class MapF { public static void main(String[] args) { Map<String, Integer> tempMap = ne ...

  6. CodeForces-722C Destroying Array 并查集 离线操作

    题目链接:https://cn.vjudge.net/problem/CodeForces-722C 题意 给个数组,每次删除一个元素,删除的元素作为一个隔断,问每次删除后该元素左右两边最大连续和 思 ...

  7. Git日常操作指令

    1. 将本地项目上传到码云: ①. 码云上创建一个项目 ②. 本地文件项目内右键git bash进入git控制台 ③. git init 命令   -- 会在本地创建一个.git文件夹 ④. git ...

  8. 虚拟机virtualbox,直接复制本机虚拟硬盘vdi使用, 会提示错误的解决方法

    提示语句为: 打开硬盘文件D:\Virtualbox\debian9 - 副本.vdi 失败. 明细(D) Cannot register the hard disk ‘D:\Virtualbox\d ...

  9. ERROR in xxxx.js from UglifyJS——配置版本混杂版

    常规解决套路可以参考这篇:https://segmentfault.com/a/11... 我采用了上面的做法,依然没法解决.我采用的是vue-cli脚手架自动生成的项目结构: vue-cli版本 2 ...

  10. 没用私服,自己安装下本地jar包,制作坐标

    没用私服的话,自己安装下本地jar包吧.执行下面命令即可. -Dfile 代表jar存在路径.mvn install:install-file -DgroupId=alipay -DartifactI ...