Zuul请求过滤
在介绍完 Zuul 的核心功能之一的路由之后,我再介绍另一个核心功能,请求过滤。在这个功能中,常见的使用场景是鉴权以及限流。本节会对鉴权与限流做一个介绍。
鉴权
为了验证 Zuul 的功能,我们还是通过示例进行说明。
pre过滤实例
定义一个简单的过滤器,在请求被路由之前先检查 HttpServletRequest 中是否存在 token 参数。如果存在,则进行路由,否则拒绝访问,返回 401。首先,新建一个 filter 包,然后在包下新建一个类,代码如下所示。
package com.cloudtest.gateway.filter;
@Component
public class TokenFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletRequest request=requestContext.getRequest();
String token=request.getParameter(“token”);
if(StringUtils.isEmpty(token)){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
}
return null;
}
}
在上面的代码中,需要继承 ZuulFilter 抽象类,并重写几个方法,下面对这几个方法进行说明。
-
filterType:过滤的类型。这个方法决定过滤器存在请求的哪个生命周期。在这里,要在请求被路由之前,进行过滤,所以选择使用 pre。
-
filterOrder:这个方法返回执行顺序的数字,这个数字越小,则过滤器越小。建议使用 FilterConstants 中的变量来减。
-
shouldFilter:判断这个过滤器是否需要被执行,在这里明显需要设置为 true。
-
run:这个方法是写逻辑的部分。
还有最后一点,这个过滤器 TokenFilter 写完不会生效,我们需要在类上添加注解 @Component,使得过滤器生效。访问链接 http://localhost:8066/myconsumerService/consumer?number=2&token=22 ,这样才可以进行路由转发。
post过滤实例
在 filter 包下,新建一个类,代码如下所示。
package com.cloudtest.gateway.filter;
//需要添加到IOC容器中
@Component
public class AddResponseFilter extends ZuulFilter {
@Override
public String filterType() {
return POST_TYPE;
}
@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER-1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
//获取RequestContext
RequestContext requestContext=RequestContext.getCurrentContext();
HttpServletResponse response=requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
上面的代码,使用的类型是 post。通过 RequestContext 获取 context,然后在里面再获取返回结果,最后在返回结果中添加 header。
访问链接 http://localhost:8066/myconsumerService/consumer?number=2&token=22 ,然后,观察结果,如图15.11所示。

限流
因为每个服务请求都会经过网关,所以比较适合做限流保护,防止网络攻击。这个限流也是在前置过滤器中,具体来说是在请求被转发之前被调用,而且限流还应该早于鉴权过滤器。
使用的算法是令牌桶算法。继续在 filter 包下新建类,代码如下所示。
package com.cloudtest.gateway.filter;
public class RateFilter extends ZuulFilter {
private static final RateLimiter RATE_LIMITER=RateLimiter.create(100);
@Override
//过滤器类型
public String filterType() {
return PRE_TYPE;
}
@Override
//过滤器执行顺序
public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER-1;
}
@Override
//是否执行
public boolean shouldFilter() {
return true;
}
@Override
//逻辑运行方法
public Object run() throws ZuulException {
//没有拿到
if(!RATE_LIMITER.tryAcquire()){
throw new RuntimeException();
}
return null;
}
}
这里对上面的代码做一个解释说明。限流的算法由 Google 的 RateLimiter 类提供,然后在 run 的逻辑中使用 tryAcquire 方法判断是否获取令牌。如果没有获取令牌,则抛出异常即可。我们在上面使用了 pre 前置过滤器,但是需要特别注意的是确定执行顺序,在这里需要选默认执行时间最早的值然后减一,做到最早执行。