在上一篇 Spring Cloud 应用篇 之 Spring Cloud Zuul(一)路由功能 中,讲解了 Zuul 的路由功能,这一篇讲解 Zuul 的过滤功能。
(一)简介
Zuul 允许开发者在 API 网关上通过定义过滤器来实现对请求的拦截与过滤,实现的方法非常简单,我们只需继承 ZuulFilter 抽象类并实现它定义的 4 个 抽象函数就可以完成对请求的拦截和过滤了。Zuul 定义了下面四种过滤器
- 前置(Pre)
- 路由(Route)
- 后置(Post)
- 错误(Error)
(二)实现前置(Pre)过滤器
2.1 创建过滤器
这里我们仅是做案例测试,所以仅在请求被路由之前检查 HttpServletRequest 中是否有 token 参数,若有就进行路由,若没有就拒绝访问,返回 401 Unauthorized 错误。在 spring-cloud-gateway 模块中创建 PreFilter 类并继承 ZuulFilter,具体实现代码如下:
-
@Component
-
public
class PreFilter extends ZuulFilter {
-
-
private
static Logger log = LoggerFactory.getLogger(PreFilter.class);
-
-
@Override
-
public String filterType() {
-
return PRE_TYPE;
//过滤器类型 : pre
-
}
-
-
@Override
-
public int filterOrder() {
-
return PRE_DECORATION_FILTER_ORDER -
5;
//过滤器执行顺序,数字越小,优先级越高,越靠前,这里设置为 0
-
}
-
-
@Override
-
public boolean shouldFilter() {
-
return
true;
//返回 true,拦截所有 URL
-
}
-
-
@Override
-
public Object run() throws ZuulException {
-
RequestContext requestContext = RequestContext.getCurrentContext();
-
HttpServletRequest request = requestContext.getRequest();
-
-
log.info(
“send {} request to {}”, request.getMethod(), request.getRequestURL().toString());
-
-
String token = request.getParameter(
“token”);
-
if (StringUtils.isEmpty(token)) {
-
log.warn(
“token is empty”);
-
requestContext.setSendZuulResponse(
false);
-
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
-
return
null;
-
}
-
log.info(
“token is ok”);
-
return
null;
-
}
-
}
在上面实现的过滤器代码中,我们通过继承 ZuulFilter 抽象类并重写下面 4 个方法来实现自定义的过滤器。这 4 个方法分别定义了如下内容。
- filterType:过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为 pre,代表会在请求被路由之前执行。
- filterOrder:过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回值来依次执行。
- shouldFilter:判断该过滤器是否需要被执行。这里直接返回了 true,因此该过滤器对所有请求都会生效。实际运用中可以利用该函数来指定过滤器的有效范围。
- run:过滤器的具体逻辑。这里通过 requestContext.setSendZuulResponse(false) 令 Zuul 过滤该请求,不对其进行路由,然后通过 requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()) 设置了其返回的错误码,当然也可以对返回的结果进行优化,比如,通过 requestContext.setResponseBody(body) 对返回的 body 内容进行编辑等。
2.2 验证过滤器
访问 http://localhost:9001/demoService/port,这个请求的 URL 并没有 token 参数,所以请求被拦截,结果如下
从后台日志也可以看出,请求被拦截
下面访问 http://localhost:9001/demoService/port?token=123,这个 URL 有 token 参数,所以顺利被 Zuul 路由,结果如下
从后台日志中也可以看出,请求没有被拦截
(三)简单实现后置(Post)过滤器
3.1 创建过滤器
这里简单的对请求的 response 设置一个 header,代码如下,实现逻辑和 Pre 过滤器类似
-
@Component
-
public
class PostFilter 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.getCurrentContext();
-
HttpServletResponse response = requestContext.getResponse();
-
response.setHeader(
"geny",
"shmily");
-
return
null;
-
}
-
}
3.2 验证过滤器
访问 http://localhost:9001/demoService/port?token=123,可以看到,对 response 设置的 header 已经成功返回。