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