一辈子也不容错过学习的微服务网关与用户身份识别,Zuul过滤器

Zuul过滤器

Spring Cloud Zuul除了可以实现请求的路由功能外,还有一个重要的功能就是过滤器。Zuul可以通过定义过滤器来实现请求的拦截和过滤,而它本身的大部分功能也是通过过滤器实现的。

 Zuul网关的过滤器类型

Zuul中定义了4种标准过滤器类型,分别说明如下:

1.pre类型的过滤器

此类型为请求路由之前调用的过滤器,可利用此类过滤器来实现身份验证、记录调试信息等。

2.route类型的过滤器

此类型为发送请求到上游服务的过滤器,比如使用ApacheHttpClient或Netflix Ribbon请求上游服务。

3.post类型的过滤器

此类型为上游服务返回之后调用的过滤器,可用来为响应添加HTTP响应头、收集统计信息和指标、将响应回复给客户端。

4.error类型的过滤器

此类型为在其他阶段发生错误时执行的过滤器。

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

Zuul的请求处理流程如下:

(1)当外部请求到达Zuul网关时,首先会进入pre处理阶段,在这个阶段请求将被pre类型的过滤器处理,以完成再请求路由的前置过滤处理,比如请求的校验等。在完成pre类型的过滤处理之后,请求进入第二个阶段:route路由请求转发阶段。

(2)在route路由请求转发阶段,请求将被route类型的过滤器处理,route类型的过滤器将外部请求转发到上游的服务。当服务实例的结果返回之后,route阶段完成,请求进入第三个阶段:post处理阶段。

(3)在post处理阶段,请求将被post类型的过滤器处理,post类型的过滤器在处理的时候不仅可以获取请求信息,还能获取服务实例的返回信息,所以post阶段可以对处理结果进行一些加工或转换等。

(4)还有一个特殊的阶段error,在该阶段请求将被error类型的过滤器处理,在上述3个阶段发生异常时才会触发,但是error过滤器也能将最终结果返回给请求客户端。

Zuul的请求处理流程如图6-3所示。

图6-3 Zuul的请求处理流程

Zuul提供了一个动态读取、编译和运行过滤器的框架。过滤器不直接相互通信,而是通过RequestContext共享状态,RequestContext(请求上下文)实例对每个请求都是唯一的。

 实战:用户的黑名单过滤

Zuul提供了一个过滤器ZuulFilter抽象基类,可以作为自定义过滤器的父类。定制一个过滤器需要实现的父类方法有4个,具体如下。

1.filterType方法

返回自定义过滤器的类型,以常量的形式定义在FilterConstants类中,具体代码如下:

package org.springframework.cloud.netflix.zuul.filters.support;
...
/**
 *@author Spencer Gibb
 */
public class FilterConstants {
 ...
 /**
 *异常过滤
 */
 public static final String ERROR_TYPE = "error";
 /**
 *后置过滤
 */
 public static final String POST_TYPE = "post";
 /**
 *前置过滤
 */
 public static final String PRE_TYPE = "pre";
 /**
 *路由过滤
 */
 public static final String ROUTE_TYPE = "route";
 ...
}

2.filterOrder方法

返回过滤器顺序,值越小优先级越高。

3.shouldFilter方法

返回过滤器是否生效的boolean值,返回true代表生效,返回false代表不生效。比如,在请求处理过程中,需要根据请求中是否携带某个参数来判断是否需要过滤时,可以用shouldFilter方法对请求进行参数判断,并返回一个相应的boolean值。

如果直接返回true,那么该过滤器总是生效。

4.run方法

过滤器的处理逻辑。在该函数中,可以进行当前的请求拦截和参数定制,也可以进行后续的路由定制,同时可以进行返回结果的定制,等等。

下面是根据请求参数username进行用户黑名单过滤的例子,如果username的参数值在黑名单中,就对请求进行拦截。具体的代码如下:

package com.crazymaker.springcloud.cloud.center.zuul.filter;
//省略import
/**
 *演示过滤器:黑名单过滤
 */
@Slf4j
@Component
public class DemoFilter extends ZuulFilter
{
 /**
 *示例所使用的黑名单:实际使用场景,需要从数据库或者其他来源获取
 */
 static List<String> blackList = Arrays.asList("foo", "bar", "test");
 /**过滤的执行类型*/
 @Override
 public String filterType()
 {
//pre:路由之前
//routing:路由之时
//post:路由之后
//error:发送错误调用
 return "pre";
 } /**
 *过滤的执行次序
 */
 @Override
 public int filterOrder()
 {
 return 0;
 }
 /**
 *这里是判断逻辑—是否要执行过滤,true为跳过
 */
 @Override
 public boolean shouldFilter()
 {
 /***获取上下文*/
 RequestContext ctx = RequestContext.getCurrentContext();
 /***如果请求已经被其他的过滤器终止,本过滤器就不做处理*/
 if (!ctx.sendZuulResponse())
 {
 return false;
 }
 /**
 *获取请求
 */
 HttpServletRequest request = ctx.getRequest();
 /**
 *返回true表示需要执行过滤器的run方法
 */
 if (request.getRequestURI().startsWith("/ZuulFilter/demo"))
 {
 return true;
 }
 /**
 *返回false表示需要跳过此过滤器,不执行run方法
 */
 return false;
 }
 /**
 *过滤器的具体逻辑
 *通过请求中的用户名称参数判断是否在黑名单中
 */
 @Override
 public Object run()
 {
 RequestContext ctx = RequestContext.getCurrentContext();
 HttpServletRequest request = ctx.getRequest();
 /**
 *对用户名称进行判断
 *如果用户名称在黑名单中,就不再转发给后端的服务提供者
 */
 String username = request.getParameter("username");
 if (username != null && blackList.contains(username))
 {
 log.info(username + " is forbidden:" +
request.getRequestURL().toString());
 /**
 *终止后续的访问流程
 */
 ctx.setSendZuulResponse(false);
 try
 {
 ctx.getResponse().setContentType("text/html;charset=utf-8");
对不起
您已经进入黑名单 ctx.getResponse().getWriter().write("对不起,您已经进入黑名单");
 } catch (Exception e)
 {
 e.printStackTrace();
 }
 return null;
 }
 return null;
 }
}

在上面的代码中,
RequestContext.setSendZuulResponse(Boolean)方法在请求上下文中设置了标志位sendZuulResponse的值为false,表示不需要后续处理。上下文setSendZuulResponse标志位的值通过RequestContext.sendZuulResponse()方法获取。

Zuul内置的几乎所有过滤器都会对该标志位进行判断,如果其值为false,那么将不用对请求进行过滤处理。以非常重要的route类型RibbonRoutingFilter为例来看其shouldFilter方法的源码,具体代码如下:

package org.springframework.cloud.netflix.zuul.filters.route;
...
public class RibbonRoutingFilter extends ZuulFilter {
 ...
 @Override
 public boolean shouldFilter() {
 RequestContext ctx = RequestContext.getCurrentContext();
 return (ctx.getRouteHost() == null && ctx.get(SERVICE_ID_KEY) != null
 && ctx.sendZuulResponse());
 }
 ...
}

以上过滤器RibbonRoutingFilter的作用是通过结合Ribbon和Hystrix来向服务提供者实例发起请求,并将请求结果返回。它的判断条件中就有sendZuulResponse的标志位判断的部分,如果该值为false,就不再发起请求。

全部评论

相关推荐

牛客737698141号:他们可以看到在线简历的。。。估计不合适直接就拒了
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务