• 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类。

  • 相关阅读:
    手游营销的10个靠谱方向
    创业三个月总结---记录这三个月的酸甜苦辣咸!!!
    创业三个月总结---记录这三个月的酸甜苦辣咸!!!
    Notepad++ 删除空白行的方法
    Notepad++ 删除空白行的方法
    几种常见容器比较和分析 hashmap, map, vector, list ...hash table
    hdu 3371
    hdu 1879
    hdu 1875
    hdu 1863
  • 原文地址:https://www.cnblogs.com/shileibrave/p/14453054.html
Copyright © 2020-2023  润新知