Spring Cloud学习笔记【八】服务网关 Zuul(过滤器)

 

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

Filter 的做用

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

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

Filter 的生命周期

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

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

  • 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() 方法中添加具体业务逻辑,具体以下:并发

 1 package com.carry.springcloud;  2  3 import javax.servlet.http.HttpServletRequest;  4  5 import com.netflix.zuul.ZuulFilter;  6 import com.netflix.zuul.context.RequestContext;  7  8 public class MyFilter extends ZuulFilter {  9 10 /** 11  * 过滤器的类型,它决定过滤器在请求的哪一个生命周期中执行。 这里定义为pre,表明会在请求被路由以前执行。 12  * 13  * @return 14 */ 15  @Override 16 public String filterType() { 17 return "pre"; 18  } 19 20 /** 21  * filter执行顺序,经过数字指定。 数字越大,优先级越低。 22  * 23  * @return 24 */ 25  @Override 26 public int filterOrder() { 27 return 0; 28  } 29 30 /** 31  * 判断该过滤器是否须要被执行。这里咱们直接返回了true,所以该过滤器对全部请求都会生效。 实际运用中咱们能够利用该函数来指定过滤器的有效范围。 32  * 33  * @return 34 */ 35  @Override 36 public boolean shouldFilter() { 37 return true; 38  } 39 40 /** 41  * 过滤器的具体逻辑 42  * 43  * @return 44 */ 45  @Override 46 public Object run() { 47 RequestContext ctx = RequestContext.getCurrentContext(); 48 HttpServletRequest request = ctx.getRequest(); 49 50 String token = request.getParameter("token"); 51 if (token == null || token.isEmpty()) { 52 ctx.setSendZuulResponse(false); 53 ctx.setResponseStatusCode(401); 54 ctx.setResponseBody("token is empty"); 55  } 56 return null; 57  } 58 }

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

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

在实现了自定义过滤器以后,它并不会直接生效,咱们还须要为其建立具体的 Bean 才能启动该过滤器,好比,在应用主类中增长以下内容:ide

 1 package com.carry.springcloud;  2  3 import org.springframework.boot.SpringApplication;  4 import org.springframework.boot.autoconfigure.SpringBootApplication;  5 import org.springframework.cloud.netflix.zuul.EnableZuulProxy;  6 import org.springframework.context.annotation.Bean;  7  8 @EnableZuulProxy  9 @SpringBootApplication 10 public class ServiceApiGatewayApplication { 11 12 public static void main(String[] args) { 13 SpringApplication.run(ServiceApiGatewayApplication.class, args); 14  } 15 16  @Bean 17 public MyFilter myFilter() { 18 return new MyFilter(); 19  } 20 }

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

 

相关文章
相关标签/搜索