6Zuul 过滤器拦截顺序 · SpringCloud微服务实战 · 看云

6. Zuul 过滤器拦截顺序

导航

本节代码地址


为了演示数据拦截顺序,我们再建一个新的过滤器

6.1 新建过滤器

新建一个过滤器,设置执行顺序为5,在TokenFilter之后,是不是这样,待会可以验证一下,这个过滤器是验证header中是否有name这个key,并且key的值要是zuul才能通过,否则失败。


@Slf4j
public class ZuulFilter extends com.netflix.zuul.ZuulFilter {
    @Override
    public String filterType() {
        return "pre"; 
    }

    @Override
    public int filterOrder() {
        return 5; 
    }

    @Override
    public boolean shouldFilter() {
        return true; 
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info("我是ZuulFilter");
        String token = request.getHeader("name");

        
        if (StringUtils.isNotBlank(token) && token.equals("zuul")) {
            ctx.setSendZuulResponse(true); 
            ctx.setResponseStatusCode(200);
            ctx.set("code", 1);
        } else {
            ctx.setSendZuulResponse(false); 
            ctx.setResponseStatusCode(401);
            HttpServletResponse response = ctx.getResponse();
            response.setHeader("content-type", "text/html;charset=utf8");
            ctx.setResponseBody("请携带网关必须参数");
            ctx.set("code", 0);
        }
        return null;
    }
}

6.2 添加过滤器配置

@Bean
public ZuulFilter zuulFilter(){
    return new ZuulFilter();
}

6.3 重启项目

浏览器或Postman输入localhost:8679/ribbon/user/1

首先全部设置正确的header

43d6c46bf0b834f7f0af746257cccb7a_MD5.png

并且我们看到控制台输出的日志,确实是filterOrder越小越先执行

2020-01-04 18:14:11.379  INFO 10940 --- [nio-8679-exec-2] c.yisu.gateways.zuul.filter.TokenFilter  : 我是TokenFilter
2020-01-04 18:14:11.385  INFO 10940 --- [nio-8679-exec-2] c.yisu.gateways.zuul.filter.ZuulFilter   : 我是ZuulFilter

去掉token 验证

8140b8c04b6461ad6da16aa218c3e65c_MD5.png

发现虽然验证失败,按理说应该不会执行第二个过滤器,但是执行了,看控制台日志

2020-01-04 18:23:49.714  INFO 10940 --- [nio-8679-exec-5] c.yisu.gateways.zuul.filter.TokenFilter  : 我是TokenFilter
2020-01-04 18:23:49.715  INFO 10940 --- [nio-8679-exec-5] c.yisu.gateways.zuul.filter.ZuulFilter   : 我是ZuulFilter

原因是什么呢?

ZuulFilter的执行逻辑如下:在ZuuLServlet中的service方法
中执行对应的Filter,比如preRoute()preRoute()中会通过zuulRunner来执行
首先请求经过ZuulServlet执行

@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    try {
        init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

        
        
        RequestContext context = RequestContext.getCurrentContext();
        context.setZuulEngineRan();

        try {
            preRoute();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            route();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            postRoute();
        } catch (ZuulException e) {
            error(e);
            return;
        }

    } catch (Throwable e) {
        error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
    } finally {
        RequestContext.getCurrentContext().unset();
    }
}


执行pre类型的过滤器


void preRoute() throws ZuulException {
    zuulRunner.preRoute();
}

调用FilterProcessorpreRoute()

public void preRoute() throws ZuulException {
    FilterProcessor.getInstance().preRoute();
}

然后preRoute()调用runFilters()获取所有过滤器并执行

public void preRoute() throws ZuulException {
    try {
        this.runFilters("pre");
    } catch (ZuulException var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
    }
}

public Object runFilters(String sType) throws Throwable {
    if (RequestContext.getCurrentContext().debugRouting()) {
        Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
    }

    boolean bResult = false;
    List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
    if (list != null) {
        for(int i = 0; i < list.size(); ++i) {
            ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
            Object result = this.processZuulFilter(zuulFilter);
            if (result != null && result instanceof Boolean) {
                bResult |= (Boolean)result;
            }
        }
    }

    return bResult;
}

由此,可以知道为什么第一个报错了,为什么第二个过滤器也执行。

那么如何让第一个停了之后不执行第二个过滤器了呢?请看下一节数据传递