Spring Cloud 之 Zuul网关Filter过滤器应用(十五)

参考1:https://blog.csdn.net/forezp/article/details/76211680

参考2:https://www.pianshen.com/article/92501046155/

上一篇我们搭建了一个基本的Zuul网关,并实现了统一访问,同时提到Zuul网关有四种过滤器:前置(Pre),路由(Route),后置(Post),错误(Error)。

知道每一种过滤器都是干什么用的,对于我们搭建一个网关非常重要。

  • 前置(Pre):这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • 路由(Route):这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  • 后置(Post):这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • 错误(Error):在其他阶段发生错误时执行该过滤器。

在开始之前有必要说明几个关键的类:

  • ServletWrappingController :该类的是Spring MVC中的请求封装控制器,负责将请求转给指定的servlet处理。
  • ZuulController:Zuul中提供的继承ServletWrappingController的子类,用将请求转到ZuulServlet处理
  • ZuulServlet:Zuul网关中网络请求处理器
  • ZuulRunner:将HttpServletRequest添加到请求的上下文中
  • FilterProcessor:集中处理各生命周期的过滤器
  • FilterLoader:存储和加载过滤器的地方
ZuulServlet源码(删减版),从源码中可以看出过滤器的调用顺序。
 1     @Override
 2     public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
 3         try {
 4             init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
 5 
 6             // Marks this request as having passed through the "Zuul engine", as opposed to servlets
 7             // explicitly bound in web.xml, for which requests will not have the same data attached
 8             RequestContext context = RequestContext.getCurrentContext();
 9             context.setZuulEngineRan();
10 
11             try {
12                 preRoute();
13             } catch (ZuulException e) {
14                 error(e);
15                 postRoute();
16                 return;
17             }
18             try {
19                 route();
20             } catch (ZuulException e) {
21                 error(e);
22                 postRoute();
23                 return;
24             }
25             try {
26                 postRoute();
27             } catch (ZuulException e) {
28                 error(e);
29                 return;
30             }
31 
32         } catch (Throwable e) {
33             error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
34         } finally {
35             RequestContext.getCurrentContext().unset();
36         }
37     }

Zuul Request请求处理流程:

 

下面通过例子演示。

1、创建一个前置过滤器,源码如下:

前置过滤器判断请求参数里是否有token字段,如果token为空则过滤掉该请求,并且返回401,Unauthorized。

 1 /**
 2  * @author Leo
 3  */
 4 @Component
 5 public class PreFilter extends ZuulFilter {
 6 
 7     private static Logger log = LoggerFactory.getLogger(PreFilter.class);
 8 
 9     /**
10      * 过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为 pre,代表会在请求被路由之前执行
11      *
12      * @return
13      */
14     @Override
15     public String filterType() {
16         return PRE_TYPE;
17     }
18 
19     /**
20      * 过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回值来依次执行。
21      *
22      * @return
23      */
24     @Override
25     public int filterOrder() {
26         //过滤器执行顺序,数字越小,优先级越高
27         return PRE_DECORATION_FILTER_ORDER - 5;
28     }
29 
30     /**
31      * 判断该过滤器是否需要被执行。这里直接返回了 true,因此该过滤器对所有请求都会生效。实际运用中可以利用该函数来指定过滤器的有效范围。
32      *
33      * @return
34      */
35     @Override
36     public boolean shouldFilter() {
37         //返回 true,拦截所有 URL
38         return true;
39     }
40 
41     /**
42      * 过滤器的具体逻辑。
43      * 这里通过设置requestContext.setSendZuulResponse(false)过滤该请求,不对其进行路由,
44      * 然后通过requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()) 设置了其返回的错误码。
45      * 当然也可以对返回的结果进行优化,比如,通过requestContext.setResponseBody(body) 对返回的 body 内容进行编辑等。
46      *
47      * @return
48      * @throws ZuulException
49      */
50     @Override
51     public Object run() throws ZuulException {
52         RequestContext requestContext = RequestContext.getCurrentContext();
53         HttpServletRequest request = requestContext.getRequest();
54 
55         log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
56 
57         String token = request.getParameter("token");
58         if (StringUtils.isEmpty(token)) {
59             log.warn("token is empty");
60             requestContext.setSendZuulResponse(false);
61             requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
62             return null;
63         }
64         log.info("token is ok");
65         return null;
66     }
67 }

2、重启网关,验证前置过滤器是否生效

访问URL:http://localhost:9001/x-demo-service-ribbon/ribbon/service,返回401说明前置过滤器生效

网关后台日志输出:

2021-02-26 16:41:11.711  INFO 66924 --- [nio-9001-exec-1] com.x.gateway.zuul.filter.PreFilter      : send GET request to http://localhost:9001/x-demo-service-ribbon/ribbon/service
2021-02-26 16:41:11.712  WARN 66924 --- [nio-9001-exec-1] com.x.gateway.zuul.filter.PreFilter      : token is empty
2021-02-26 16:41:15.571  INFO 66924 --- [nio-9001-exec-2] com.x.gateway.zuul.filter.PreFilter      : send GET request to http://localhost:9001/x-demo-service-ribbon/ribbon/service
2021-02-26 16:41:15.571  WARN 66924 --- [nio-9001-exec-2] com.x.gateway.zuul.filter.PreFilter      : token is empty

请求参数加上token再试一次:http://localhost:9001/x-demo-service-ribbon/ribbon/service?token=123

可以看到可以正常访问了,过滤器生效。

 关于其它类型的过滤器,大家可以自己试试。原理一样,都是继承ZuulFilter类。

原文地址:https://www.cnblogs.com/shileibrave/p/14453054.html