zoukankan      html  css  js  c++  java
  • zuul filter

    前言

    过滤器是Zuul的核心组件,这篇文章我们来详细讨论Zuul的过滤器。下面话不多说,来看看详细的介绍吧。

    过滤器类型与请求生命周期

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

    (1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

    (2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

    (3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

    (4) ERROR:在其他阶段发生错误时执行该过滤器。

    除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

    通过Filter,我们可以实现安全控制,比如,只有请求参数中有用户名和密码的客户端才能访问服务端的资源。那么如何来实现Filter了?

    要想实现Filter,需要以下几个步骤:

    1、继承ZuulFilter类,为了验证Filter的特性,我们这里创建3个Filter

    根据用户名来过滤

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.chhliu.springcloud.zuul;  
    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 AccessUserNameFilter extends ZuulFilter {  
    9.     @Override  
    10.     public Object run() {  
    11.         RequestContext ctx = RequestContext.getCurrentContext();  
    12.         HttpServletRequest request = ctx.getRequest();  
    13.   
    14.         System.out.println(String.format("%s AccessUserNameFilter request to %s", request.getMethod(), request.getRequestURL().toString()));  
    15.   
    16.         String username = request.getParameter("username");// 获取请求的参数  
    17.         if(null != username && username.equals("chhliu")) {// 如果请求的参数不为空,且值为chhliu时,则通过  
    18.             ctx.setSendZuulResponse(true);// 对该请求进行路由  
    19.             ctx.setResponseStatusCode(200);  
    20.             ctx.set("isSuccess", true);// 设值,让下一个Filter看到上一个Filter的状态  
    21.             return null;  
    22.         }else{  
    23.             ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由  
    24.             ctx.setResponseStatusCode(401);// 返回错误码  
    25.             ctx.setResponseBody("{"result":"username is not correct!"}");// 返回错误内容  
    26.             ctx.set("isSuccess", false);  
    27.             return null;  
    28.         }  
    29.     }  
    30.   
    31.     @Override  
    32.     public boolean shouldFilter() {  
    33.         return true;// 是否执行该过滤器,此处为true,说明需要过滤  
    34.     }  
    35.   
    36.     @Override  
    37.     public int filterOrder() {  
    38.         return 0;// 优先级为0,数字越大,优先级越低  
    39.     }  
    40.   
    41.     @Override  
    42.     public String filterType() {  
    43.         return "pre";// 前置过滤器  
    44.     }  
    45. }  

    通过继承ZuulFilter然后覆写上面的4个方法,就可以实现一个简单的过滤器,下面就相关注意点进行说明

    filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:

    • pre:可以在请求被路由之前调用
    • route:在路由请求时候被调用
    • post:在route和error过滤器之后被调用
    • error:处理请求时发生错误时被调用

         Zuul的主要请求生命周期包括“pre”,“route”和“post”等阶段。对于每个请求,都会运行具有这些类型的所有过滤器。

    filterOrder:通过int值来定义过滤器的执行顺序

    shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效

    run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码

    过滤器间的协调
        过滤器没有直接的方式来访问对方。 它们可以使用RequestContext共享状态,这是一个类似Map的结构,具有一些显式访问器方法用于被认为是Zuul的原语,内部是使用ThreadLocal实现的,有兴趣的同学可以看下源码。

    再建一个过滤器,根据密码来过滤:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.chhliu.springcloud.zuul;  
    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 AccessPasswordFilter extends ZuulFilter {  
    9.   
    10.     @Override  
    11.     public Object run() {  
    12.         RequestContext ctx = RequestContext.getCurrentContext();  
    13.         HttpServletRequest request = ctx.getRequest();  
    14.   
    15.         System.out.println(String.format("%s AccessPasswordFilter request to %s", request.getMethod(), request.getRequestURL().toString()));  
    16.   
    17.         String username = request.getParameter("password");  
    18.         if(null != username && username.equals("123456")) {  
    19.             ctx.setSendZuulResponse(true);  
    20.             ctx.setResponseStatusCode(200);  
    21.             ctx.set("isSuccess", true);  
    22.             return null;  
    23.         }else{  
    24.             ctx.setSendZuulResponse(false);  
    25.             ctx.setResponseStatusCode(401);  
    26.             ctx.setResponseBody("{"result":"password is not correct!"}");  
    27.             ctx.set("isSuccess", false);  
    28.             return null;  
    29.         }  
    30.     }  
    31.   
    32.     @Override  
    33.     public boolean shouldFilter() {  
    34.         RequestContext ctx = RequestContext.getCurrentContext();  
    35.         return (boolean) ctx.get("isSuccess");// 如果前一个过滤器的结果为true,则说明上一个过滤器成功了,需要进入当前的过滤,如果前一个过滤器的结果为false,则说明上一个过滤器没有成功,则无需进行下面的过滤动作了,直接跳过后面的所有过滤器并返回结果  
    36.     }  
    37.   
    38.     @Override  
    39.     public int filterOrder() {  
    40.         return 1; // 优先级设置为1  
    41.     }  
    42.   
    43.     @Override  
    44.     public String filterType() {  
    45.         return "pre";  
    46.     }  
    47. }  

    最后建一个post过滤器

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.chhliu.springcloud.zuul;  
    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 AccessTokenFilter extends ZuulFilter {  
    9.     @Override  
    10.     public Object run() {  
    11.         RequestContext ctx = RequestContext.getCurrentContext();  
    12.         HttpServletRequest request = ctx.getRequest();  
    13.   
    14.         System.out.println(String.format("%s AccessTokenFilter request to %s", request.getMethod(),  
    15.                 request.getRequestURL().toString()));  
    16.           
    17.         ctx.setSendZuulResponse(true);  
    18.         ctx.setResponseStatusCode(200);  
    19.         ctx.setResponseBody("{"name":"chhliu"}");// 输出最终结果  
    20.         return null;  
    21.     }  
    22.   
    23.     @Override  
    24.     public boolean shouldFilter() {  
    25.         return true;  
    26.     }  
    27.   
    28.     @Override  
    29.     public int filterOrder() {  
    30.         return 0;  
    31.     }  
    32.   
    33.     @Override  
    34.     public String filterType() {  
    35.         return "post";// 在请求被处理之后,会进入该过滤器  
    36.     }  
    37. }  

    2、在主类中,先开启前面的两个过滤器

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. @Bean  
    2.     public AccessUserNameFilter accessUserNameFilter() {  
    3.         return new AccessUserNameFilter();  
    4.     }  
    5.       
    6.     @Bean  
    7.     public AccessPasswordFilter accessPasswordFilter(){  
    8.         return new AccessPasswordFilter();  
    9.     }  

    3、输入请求,验证

    (1)请求为:http://localhost:8768/h2service/user/1?username=chhliu

    测试结果为:

    {"result":"password is not correct!"}

    控制台打印结果

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. GET AccessUserNameFilter request to http://localhost:8768/h2service/user/1  
    2. GET AccessPasswordFilter request to http://localhost:8768/h2service/user/1  

    通过了AccessUserNameFilter过滤器,在验证AccessPasswordFilter过滤器的时候失败了

    后台无sql打印,说明请求没有被路由

    (2)请求为:http://localhost:8768/h2service/user/1?password=123456

    测试结果为:

    {"result":"username is not correct!"}

    控制台打印结果:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. GET AccessUserNameFilter request to http://localhost:8768/h2service/user/1  

    说明到了AccessUserNameFilter过滤器,但是没有到AccessPasswordFilter过滤器,因为AccessUserNameFilter过滤器的优先级高一些,会先执行,在执行的时候,发现过滤条件不符合,于是跳过了后面所有的过滤器,并返回结果

    后台无sql打印,说明请求没有被路由

    (3)请求为:http://localhost:8768/h2service/user/1?password=123456&username=chhliu

    测试结果为:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. {  
    2.   
    3.     "id": 1,  
    4.     "username": "user1",  
    5.     "name": "张三",  
    6.     "age": 20,  
    7.     "balance": 100.00  
    8.   
    9. }  

    控制台打印的结果:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. GET AccessUserNameFilter request to http://localhost:8768/h2service/user/1  
    2. GET AccessPasswordFilter request to http://localhost:8768/h2service/user/1  

    说明是先执行了AccessUserNameFilter然后才执行AccessPasswordFilter这也和我们前面说的order的值越小,优先级越高是吻合的。

    同时被请求的服务有sql输出:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.balance as balance3_0_0_, user0_.name as name4_0_0_, user0_.username as username5_0_0_ from user user0_ where user0_.id=?  

    说明请求被路由了。

    4、开启post过滤器,再跑一次

    测试结果:发现post过滤器是最后执行的,尽管它的优先级为0

    关于zuul的Filter的生命周期,见下图

    注:上图有个小错误,routing应该是route

    5、拓展

    zuul还提供了一类特殊的过滤器,分别为:StaticResponseFilter和SurgicalDebugFilter

    StaticResponseFilter:StaticResponseFilter允许从Zuul本身生成响应,而不是将请求转发到源。

    SurgicalDebugFilter:SurgicalDebugFilter允许将特定请求路由到分隔的调试集群或主机。

  • 相关阅读:
    Something I know about WebDynpro
    Details about support package implementation
    CRM Middleware Performance Topics
    Way to configure the logon navigaion layouts via Business Roles in CRM
    DOM 常用节点类型和方法
    第一届 xdef 会议日程
    去除百度音乐盒广告的chrome插件 持续更新
    从人人网抓取高校数据信息,包括,省份 高校 院系 (提供最终SQL文件下载)
    PHP 与 JSON
    解决HTTPS 发送请求走socket问题
  • 原文地址:https://www.cnblogs.com/wangjing666/p/6872591.html
Copyright © 2011-2022 走看看