查看原文
其他

Spring Cloud(七)服务网关 Zuul Filter 使用

搜云库 搜云库技术团队 2019-04-07

上一篇文章中,讲了Zuul 转发,动态路由,负载均衡,等等一些Zuul 的特性,这个一篇文章,讲Zuul Filter 使用,关于网关的作用,这里就不再次赘述了,重点是zuul的Filter ,我们可以实现安全控制,比如,只有请求参数中有token和密码的客户端才能访问服务端的资源。那么如何来实现Filter了?

Spring Cloud Zuul

zuul 执行流程

Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。

PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

OST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

ERROR:在其他阶段发生错误时执行该过滤器。

除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

准备工作

我们先拿之前两篇文章,构建的两个微服务代码为基础,进行下面的操作

建议先阅读以下两篇文章

Spring Cloud(四) 服务提供者 Eureka + 服务消费者 Feign 
Spring Cloud(三) 服务提供者 Eureka + 服务消费者(rest + Ribbon)

http://www.ymq.io/2017/12/06/spring-cloud-feign/

http://www.ymq.io/2017/12/05/spring-cloud-ribbon-rest/

Eureka Service

导入第三篇文章中的项目:作为服务注册中心

spring-cloud-eureka-service

Eureka Provider

导入第三篇文章中的项目:作为服务的提供者

spring-cloud-eureka-provider-1
spring-cloud-eureka-provider-2
spring-cloud-eureka-provider-3

简单使用

新建项目 spring-cloud-zuul-filter

添加依赖

  1. <dependency>

  2.    <groupId>org.springframework.cloud</groupId>

  3.    <artifactId>spring-cloud-starter-zuul</artifactId>

  4. </dependency>

开启服务注册

在程序的启动类 ZuulFilterApplication 通过 @EnableZuulProxy 开启 Zuul 服务网关

  1. package io.ymq.example.zuul.filter;

  2. import org.springframework.boot.SpringApplication;

  3. import org.springframework.boot.autoconfigure.SpringBootApplication;

  4. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

  5. import org.springframework.context.annotation.Bean;

  6. @EnableZuulProxy

  7. @SpringBootApplication

  8. public class ZuulFilterApplication {

  9.    public static void main(String[] args) {

  10.        SpringApplication.run(ZuulFilterApplication.class, args);

  11.    }

  12. }

添加配置

配置文件 application.yml

  1. spring:

  2.  application:

  3.    name: zuul-service-filter

  4. server:

  5.  port: 9000

  6. zuul:

  7.  routes:

  8.    api:

  9.        path: /**

  10.        serviceId: eureka-provider

  11. eureka:

  12.  client:

  13.    serviceUrl:

  14.      defaultZone: http://localhost:8761/eureka/

TokenFilter

ZuulFilter 是Zuul中核心组件,通过继承该抽象类,覆写几个关键方法达到自定义调度请求的作用

TokenFilter 过滤器

  1. package io.ymq.example.zuul.filter;

  2. import com.netflix.zuul.ZuulFilter;

  3. import com.netflix.zuul.context.RequestContext;

  4. import org.apache.commons.lang.StringUtils;

  5. import org.slf4j.Logger;

  6. import org.slf4j.LoggerFactory;

  7. import javax.servlet.http.HttpServletRequest;

  8. /**

  9. * 描述: 过滤器 token

  10. *

  11. * @author yanpenglei

  12. * @create 2017-12-11 14:38

  13. **/

  14. public class TokenFilter extends ZuulFilter {

  15.    private final Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);

  16.    @Override

  17.    public String filterType() {

  18.        return "pre"; // 可以在请求被路由之前调用

  19.    }

  20.    @Override

  21.    public int filterOrder() {

  22.        return 0; // filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低

  23.    }

  24.    @Override

  25.    public boolean shouldFilter() {

  26.        return true;// 是否执行该过滤器,此处为true,说明需要过滤

  27.    }

  28.    @Override

  29.    public Object run() {

  30.        RequestContext ctx = RequestContext.getCurrentContext();

  31.        HttpServletRequest request = ctx.getRequest();

  32.        LOGGER.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

  33.        String token = request.getParameter("token");// 获取请求的参数

  34.        if (StringUtils.isNotBlank(token)) {

  35.            ctx.setSendZuulResponse(true); //对请求进行路由

  36.            ctx.setResponseStatusCode(200);

  37.            ctx.set("isSuccess", true);

  38.            return null;

  39.        } else {

  40.            ctx.setSendZuulResponse(false); //不对其进行路由

  41.            ctx.setResponseStatusCode(400);

  42.            ctx.setResponseBody("token is empty");

  43.            ctx.set("isSuccess", false);

  44.            return null;

  45.        }

  46.    }

  47. }

PasswordFilter

ZuulFilter 是Zuul中核心组件,通过继承该抽象类,覆写几个关键方法达到自定义调度请求的作用

PasswordFilter 过滤器

  1. package io.ymq.example.zuul.filter;

  2. import com.netflix.zuul.ZuulFilter;

  3. import com.netflix.zuul.context.RequestContext;

  4. import org.slf4j.Logger;

  5. import org.slf4j.LoggerFactory;

  6. import javax.servlet.http.HttpServletRequest;

  7. /**

  8. * 描述: 过滤器 Password

  9. *

  10. * @author yanpenglei

  11. * @create 2017-12-11 15:40

  12. **/

  13. public class PasswordFilter extends ZuulFilter {

  14.    private final Logger LOGGER = LoggerFactory.getLogger(TokenFilter.class);

  15.    @Override

  16.    public String filterType() {

  17.        return "post"; // 请求处理完成后执行的filter

  18.    }

  19.    @Override

  20.    public int filterOrder() {

  21.        return 1; // 优先级为0,数字越大,优先级越低

  22.    }

  23.    @Override

  24.    public boolean shouldFilter() {

  25.        RequestContext ctx = RequestContext.getCurrentContext();

  26.        return (boolean) ctx.get("isSuccess");

  27.        // 判断上一个过滤器结果为true,否则就不走下面过滤器,直接跳过后面的所有过滤器并返回 上一个过滤器不通过的结果。

  28.    }

  29.    @Override

  30.    public Object run() {

  31.        RequestContext ctx = RequestContext.getCurrentContext();

  32.        HttpServletRequest request = ctx.getRequest();

  33.        LOGGER.info("--->>> PasswordFilter {},{}", request.getMethod(), request.getRequestURL().toString());

  34.        String username = request.getParameter("password");

  35.        if (null != username && username.equals("123456")) {

  36.            ctx.setSendZuulResponse(true);

  37.            ctx.setResponseStatusCode(200);

  38.            ctx.set("isSuccess", true);

  39.            return null;

  40.        } else {

  41.            ctx.setSendZuulResponse(false);

  42.            ctx.setResponseStatusCode(400);

  43.            ctx.setResponseBody("The password cannot be empty");

  44.            ctx.set("isSuccess", false);

  45.            return null;

  46.        }

  47.    }

  48. }

开启过滤器

在程序的启动类 ZuulFilterApplication 添加 Bean

  1. @Bean

  2. public TokenFilter tokenFilter() {

  3.    return new TokenFilter();

  4. }

  5. @Bean

  6. public PasswordFilter PasswordFilter() {

  7.    return new PasswordFilter();

  8. }

filterType

filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:

  • pre:路由之前

  • routing:路由之时

  • post: 路由之后

  • error:发送错误调用

  • filterOrder:过滤的顺序

  • shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。

  • run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。

测试服务

依次启动项目:

spring-cloud-eureka-service
spring-cloud-eureka-provider-1
spring-cloud-eureka-provider-2
spring-cloud-eureka-provider-3
spring-cloud-zuul-filter

启动该工程后,访问服务注册中心,查看服务是否都已注册成功:http://localhost:8761/

查看 eureka 监控,看服务是否都注册成功

token 测试

访问:http://127.0.0.1:8761/

步骤一 提示 tokenisempty

访问:http://127.0.0.1:9000/

步骤二 加上token ?token=token-uuid ,已经验证通过了,提示 Thepassword cannot be empty

访问:http://127.0.0.1:9000/?token=token-uuid

password 测试

![The password cannot be empty][3]

加上 token 和 password &password=123456 ,已经验证通过

访问:http://127.0.0.1:9000/?token=token-uuid&password=123456

F5 刷新,每次都验证通过,并且负载均衡

源码下载

GitHub:https://github.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul-filter

码云:https://gitee.com/souyunku/spring-cloud-examples/tree/master/spring-cloud-zuul-filter

Contact

  • 作者:鹏磊

  • 出处:http://www.ymq.io

  • Email:admin@souyunku.com

  • 版权归作者所有,转载请注明出处

  • Wechat:关注公众号,搜云库,专注于开发技术的研究与知识分享


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存