[Spring Cloud] - Zuul 实践(三) - 过滤器

在设计系统阶段,必定记住Zuul的核心思想:Zuul是作服务的路由,而不是页面的转发。这是zuul与Nginx根本上的不一样。前端

Zuul没有提供良好的跳转或转发功能,这和它的使用场景是有关的。它的核心功能包括验证服务权限,验证成功或失败,成功或失败后放行仍是拦截等等。若拦截,只需返回响应码便可,至于成功或失败的页面显示,则应该由前端负责跳转或转发。
请必定遵照标准化的设计,不要让Zuul参与页面跳转(固然,Zuul也能够实现页面跳转,本文后半部分会涉及到,但不推荐由Zuul实现跳转)网络

Zuul的验证功能基本在它的过滤器中实现。ide

过滤器类型与请求的生命周期

每个请求发送到Zuul,都有其对应的不一样阶段,能够称其为“生命周期”,好比初始化阶段,处理请求阶段,结束阶段,出error阶段等等。微服务

在谈下一话题前,请必定分清楚“转发”和“路由”的区别。
若是说定义,路由是一种策略,转发是一条路径。
说浅显一点,路由是网状结构下的各类路径解的集合,而转发是点对点的一条通讯路径。
再浅显一些,就像学生年代你上课时给女神小红递纸条,路由是你将纸条给你同桌(代理),你同桌会选择一条消息发送的路径,好比将纸条传递给A,再到B,再到C,最后到小红。由同桌发到小红的整个过程就叫路由。你只须要将纸条给同桌(代理),剩下的不须要你参与,你的同桌会选择最优路径,不管是经过ABC,仍是BAC,当纸条传递成功,你的代理会反馈给你结果(同桌会告诉你纸条传递成功,小红看都没看就扔进垃圾桶),你不须要知道中间隔了多少人。这就是路由。
而转发,就是你直接把纸条揉成团,扔给小红,小红收到后含羞一笑,你看到后心花盛开。这就是转发。
最浅显的归纳就是: 路由是多跳的转发,转发的一跳的路由。这一点从英文routing和forwading的区别就能够明白。
若是还没法理解,请从新读一遍计算机网络。工具


对于微服务项目来讲,Zuul实现的就是路由功能,每每也被会称作路由转发、网关等等,但意思都是同样的。
本文在谈Zuul的拦截器,
每个request在发送到Zuul后,都有几个不一样的阶段(生命周期),Zuul的几个拦截器对应request的不一样阶段,因此咱们先谈一下request的生命周期:post

  1. 请求刚刚被Zuul接收到,Zuul须要解析请求的地址,以及其请求参数等等。
  2. 请求即将被路由。Zuul已经肯定此请求该发送至哪个service,并已准备好将其发送过去。
  3. 请求被路由至目标服务
  4. 请求在以上三个阶段中的某一个出错。

以上四个阶段是request的生命周期,Zuul的四种拦截器分别对应以上的四个request生命周期: PRE, ROUTING, POST, ERROR.计算机网络

因此,若是想要对request的某个生命周期进行操做(过滤),只须要按需实现这四种拦截器便可。设计

那么,如何实现这四种拦截器呢?依然超级简单,答案只有一个:继承ZuulFilter类并覆写父类方法便可。
区分拦截器的类型在方法filterType()中。
闲言少叙,上代码。
如下是一个最基本的PRE拦截器。代理

实现拦截器

定义
新建一个类,继承ZuulFilter,并覆写其方法。code

public class PreRequestFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(RouteRequestFilter.class);

    // 表示此拦截器类型, pre、route、post、error
    @Override
    public String filterType() {
        return "pre";
    }
    
    // 执行顺序,如有多个同种拦截器,好比有多个pre拦截器,他们的执行顺序是什么
    @Override
    public int filterOrder() {
        return 0;
    }

    // 是否应用这个拦截器,true是应用,false表示这个拦截器是失效的
    @Override
    public boolean shouldFilter() {
        return false;
    }

    // 此方法为核心方法,表示拦截器中执行的逻辑
    @Override
    public Object run() throws ZuulException {
        System.out.println("pre");
        return null;
    }
}

注册为Bean
在主类中将此拦截器注册为Bean

@EnableZuulProxy
@SpringBootApplication
public class ZuulApp {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class, args);
    }

    @Bean
    public PreRequestLogFilter preRequestLogFilter(){
        return new PreRequestLogFilter();
    }

此时,一个最基本的拦截器就建立成功了!

拦截请求

在run方法中,添加如下内容:

@Override
    public Object run(){
        //获取上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //获取Request
        HttpServletRequest request = ctx.getRequest();
        //获取请求参数accessToken
        String accessToken = request.getParameter("accessToken");
        //使用String工具类
        if (StringUtils.isBlank(accessToken)) {
            logger.error("no token");
            logger.warn("accessToken is empty");
            ctx.setSendZuulResponse(false);  //进行拦截
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("accessToken is empty");
            } catch (IOException e) {
                e.printStackTrace();
            }

            return null;
        }
        return null;
   }

此时,若经过Zuul访问微服务,若没有accessToken参数,会发现请求被拦截,页面上会显示“accessToken is empty”

相关文章
相关标签/搜索