zuul过滤器filter 的编写

    通过上一节(zuul的各种配置)的学习,我们学会了zuul路由的各种配置,这一节我们来实现一下zuul的过滤器功能。那么为什么需要用到zuul的过滤器呢?我们知道zuul是我们实现外部系统统一访问的入口,那么我们就可以在 zuul 上实现 api的鉴权操作,实现微服务的统一鉴权、给微服务的响应增加额外的响应头等。

实现功能

    1、在网关增加一个 pre 类型的过滤器完成一个简单的权限的校验

    2、在网关增加一个 post 类型的过滤器完成增加一个相应头

    3、禁用某个过滤器

代码结构

eureka-server
    |- 服务注册中心
    zuul
        product-provider-8202
        product-provider-8203
            |- 服务提供者
        product-consumer-8201
            |- 服务消费者
        product-gateway-filters-8205
            |- 网关程序,演示 过滤器 的使用

    代码和上一节的代码差不多,只是新建了一个网关程序,在网关中增加了过滤器。

zuul过滤器的四种类型
    pre:
        这种类型的过滤器在请求被路由到具体的服务之前进行调用,这个时候我们就可以进行权限的验证等等
    route:
        这种类型的过滤器将请求路由到具体的微服务。
    post:
        路由到微服务之后的处理,比如操作响应(Response),增加相应头、输出额外的内容
    error:
        当pre、route、post阶段发生异常时进行异常的处理

过滤器中各个方法的含义

        @Override
	public String filterType() {
	}
	@Override
	public int filterOrder() {
	}
	@Override
	public boolean shouldFilter() {
	}
	@Override
	public Object run() {
		return null;
	}

    filterType:

        |- 表示过滤器的类型,即在那个阶段拦截,可以使用 FilterConstants 中的常量

    filterOrder:

        |- 过滤器的顺序,在同一组类型的过滤器中返回的值越小,越早执行。

           >> pre   类型建议在 FilterConstants.PRE_DECORATION_FILTER_ORDER  之前执行

           >> post  类型建议在 FilterConstants.SEND_RESPONSE_FILTER_ORDER  之前执行

    shouldFilter:

         |- true: 表示该过滤器执行 false:表示不执行该过滤器

    run:

         |- 执行具体的过滤器逻辑,返回值一般返回 null 即可。

    在过滤器中,如果当前请求不合法,可以跑出异常或设置 RequestContext.getCurrentContext().setSendZuulResponse(false) 来阻止 route 类型的过滤器执行。

zuul过滤器的生命周期

一、官网zuul的请求生命周期图

 

 

    解释:从上图可以看出,一次请求先经过 pre或 custom过滤器进行请求的预处理,然后通过 route 类型的过滤器将请求路由到据图的微服务,最终经过 post 类型的过滤器对响应数据进行处理。这三个阶段(pre、route、post)过程中发生了异常都会经过 error 类型的过滤器进行处理,error 处理完之后再次经过 post 类型的过滤器进行处理。

二、从代码(ZuulServlet)中看各个过滤器执行的顺序

 

代码编写

一、注册中心、服务提供者、服务消费者(略)

二、网关程序编写

       ①、引入 zuul 的依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

 

      ② 启动类上增加 @EnableZuulProxy 注解

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ProductGatewayZuulApplication8205 {

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

}

 

       ③、编写 pre 过滤器,进行权限判断

              只要能从 request 中 获取到 token 的参数的值,那么认为通过,否则不通过。

package com.huan.study.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * Token 认证前置过滤器
 *
 * @author huan.fu
 * @date 2018/6/12 - 16:34
 */
@Component
public class TokenAuthenticationFilter extends ZuulFilter {

	private static final String TOKEN_PARAMETER = "token";

	@Override
	public String filterType() {
		// pre 类型的过滤器
		return FilterConstants.PRE_TYPE;
	}

	@Override
	public int filterOrder() {
		// token 检验应该放在第一位来进行校验,因此需要放在最前面
		return FilterConstants.SERVLET_DETECTION_FILTER_ORDER - 1;
	}

	@Override
	public boolean shouldFilter() {
		// 过滤器是否应该执行, true:表示应该执行  false:表示跳过这个过滤器执行
		return true;
	}

	@Override
	public Object run() {
		RequestContext requestContext = RequestContext.getCurrentContext();
		// 获取到 request
		HttpServletRequest request = requestContext.getRequest();
		// 判断请求参数中是否存在  token  参数
		String token = request.getParameter(TOKEN_PARAMETER);
		if (StringUtils.isBlank(token)) {
			// 不进行路由 ===> 即 route 类型的过滤器不执行
			requestContext.setSendZuulResponse(false);
			throw new ZuulRuntimeException(new ZuulException(this.filterType() + ":" + this.getClass().getSimpleName(), HttpStatus.UNAUTHORIZED.value(), "token参数不可为空"));
		}
		return null;
	}
}

    注意:

            1、RequestContext 为一个 ConcurrentHashMap, 并且里面的值是从 ThreadLocal 中获取的

            2、可以从RequestContext中获取到 request 和 response 等等

            3、requestContext.setSendZuulResponse(false)导致 route 类型的过滤器不进行执行

            4、如果不想自己写 error 类型的过滤器,那么默认由 SendErrorFilter 进行处理异常,如果我们想返回一个 401 的状态码给前台,出错的地方可以跑出一个 ZuulRuntimeException.

            5、自己写的 过滤器 需要被 Spring 管理即可

      

       ④、编写一个 post 类型的过滤器,增加一个额外的响应头

             添加一个 new-header 的响应头

package com.huan.study.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;

/**
 * 添加一个新的相应头
 *
 * @author huan.fu
 * @date 2018/6/12 - 18:01
 */
@Component
public class AddNewHeaderFilter extends ZuulFilter {

	@Override
	public String filterType() {
		return FilterConstants.POST_TYPE;
	}

	@Override
	public int filterOrder() {
		return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
	}

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

	@Override
	public Object run() {
		RequestContext requestContext = RequestContext.getCurrentContext();
		HttpServletResponse response = requestContext.getResponse();
		response.addHeader("new-header", "new-header");

		return null;
	}
}

      ⑤、运行结果

 

 

禁用过滤器

    有些时候我们可能想禁用某一类型的过滤器,比如异常返回的过滤器,每个公司可能都有自己异常返回的一套风格,如果我们不想使用 Zuul 默认的异常过滤器,那么就可以禁用掉这个过滤器。

 语法格式:

zuul.<SimpleClassName>.<filterType>.disable=true

 禁用 SendErrorFilter

zuul:
  SendErrorFilter:
    error:
      disable: true

 访问网关服务的 /filters 端点,看是否禁用掉

 

完整代码

    网关层的代码: https://gitee.com/huan1993/spring-cloud-parent/tree/master/zuul

    网关过滤器的代码: https://gitee.com/huan1993/spring-cloud-parent/tree/master/zuul/product-gateway-filters-8205

posted @ 2018-06-13 11:41  huan1993  阅读(81)  评论(0编辑  收藏  举报