Spring Cloud Zuul 除了能够实现请求的路由功能,还有一个重要的功能就是过滤器。Zuul 的路由功能让全部的微服务提供的接口有统一的网关入口,但并非全部的接口都是对外彻底开发的,它们的访问权限通常都有必定的限制。那咱们能够在每一个服务都加上对应的校验和权限鉴定,那这些一般都是用过滤器或拦截器实现的,并且一个系统的各个服务的校验也大都是类似,这些类似的校验逻辑打码在每一个服务都会有一份,不只冗余且维护麻烦。更好的办法就是在请求的最前端统一去作这样的事情,而统一的 API 服务网关入口就是合适的选择前端
Zuul 能够经过定义过滤器来实现请求的拦截和过滤,而它自己的大部分功能也是经过过滤器实现的java
在 Zuul 中自定义过滤器须要继承抽象类 ZuulFilter
,须要实现如下4个方法:git
String filterType(); // 过滤器类型 int filterOrder(); // 执行顺序,数值越小优先级越高 boolean shouldFilter(); // 执行过滤器的条件 Object run() throws ZuulException; // 具体的过滤操做
从名字咱们也能知道各个方法的做用,下面来了解下过滤器类型和过滤器的生命周期,以及自定义过滤器的使用github
Zuul 定义了4种不一样的过滤器类型,对应着请求的典型生命周期web
PRE
: 在请求被路由以前执行。能够用于请求身份验证、选择源服务器和记录调试信息ROURING
: 该过滤器将请求路由到服务。用于构建和发送给微服务的请求(使用 Apache HttpClient 或 Netflix Ribbon 请求服务)POST
: 在请求被路由到服务以后执行。能够用于向响应添加标准的 HTTP Header、收集统计数据和指标,以及将响应从源服务发送到客户端ERROR
: 该过滤器在其余阶段发生错误时执行除了默认的过滤器流,Zuul 还容许咱们建立自定义过滤器类型并显式地执行它们。例如,咱们能够自定义一个 STATIC
类型的过滤器,它在Zuul 中生成响应,而不是将请求转发到后端的服务spring
下面是 Zuul 的生命周期图,描述着各类类型的过滤器的执行顺序(图片来源于 Zuul Wiki)json
Spring Cloud Zuul 做为服务网关的大部分功能都是经过过滤器实现的,它在请求的各个阶段实现了一系列的过滤器,在 Spring Cloud Zuul 网关服务启动时自动加载和启用。实现的这些过滤器是在 spring-cloud-netflix-zuul
模块中的 org.springframework.cloud.netflix.zuul.filters
包下面后端
下面介绍部分过滤器的功能服务器
Pre filtersapp
filter | order | 说明 |
---|---|---|
ServletDetectionFilter |
-3 | 检测请求是否经过 Spring 调度程序,即判断请求是交由 Spring DispatcherServlet 处理,仍是 ZuulServlet 处理(主要是用于大文件上传) |
Servlet30WrapperFilter |
-2 | 把原始 HttpServletRequest 包装成 Servlet30RequestWrapper 对象 |
FormBodyWrapperFilter |
-1 | 解析表单数据并为下游服务从新编码 |
DebugFilter |
1 | 若是设置 debug 请求参数,则此过滤器将RequestContext.setDebugRouting() 和 RequestContext.setDebugRequest() 设置为true |
PreDecorationFilter |
5 | 根据提供的 RouteLocator 肯定路由的位置和方式,它还为下游请求设置各类与代理相关的头文件 |
Route filters
filter | order | 说明 |
---|---|---|
RibbonRoutingFilter |
10 | 使用 Ribbon、Hystrix 和 可插拔 HTTP客户机发送请求。只对 RequestContext 存在 serviceId 参数的请求进行处理,即只对经过 serviceId配置路由规则的请求路由。可使用不一样的 HTTP 客户端:HttpClient、OkHttpClient、Netflix Ribbon HTTP client |
SimpleHostRoutingFilter |
100 | 经过 Apache HttpClient 发送请求到预约的 url,这些 url 能够在 RequestContext.getRouteHost() 中找到,即只对经过 url 配置路由规则的请求路由 |
SendForwardFilter |
500 | 经过使用 Servlet RequestDispatcher 转发请求。用于转发请求到当前应用的端点 |
Post filters
filter | order | 说明 |
---|---|---|
LocationRewriteFilter |
900 | 负责将 Location header 重写为 Zuul URL |
SendResponseFilter |
1000 | 将代理请求的响应写入当前响应 |
Error filters
filter | order | 说明 |
---|---|---|
SendErrorFilter |
0 | 利用请求上下文中的错误信息来组织成一个 forward 到 /error 错误端点的请求来产生错误响应 |
禁用过滤器
默认状况下,这些过滤器在代理和服务器模式下都是启用的。若是在某些场景下,禁用某个过滤器,能够设置
zuul.<SimpleClassName>.<filterType>.disable=true
。例如,要禁用org.springframework.cloud.netflix.zuul.filter.post.sendresponsefilter
,设置zuul.SendResponseFilter.post.disable=true
建立一个 Spring Boot 项目 zuul-filters
,Zuul 的服务网关路由配置能够见 Spring Cloud Zuul 构建微服务网关
spring: application: name: zuul-filters server: port: 8090 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ zuul: ignoredServices: '*' routes: product: path: /product/** serviceId: product-service management: endpoints: web: exposure: include: '*'
自定义一个过滤器 AccessFilter
,对请求中没有 accessToken 参数的请求,返回 401拒绝访问
@Log4j2 public class AccessFilter extends ZuulFilter { @Override public int filterOrder() { // run before PreDecoration return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; } @Override public String filterType() { return FilterConstants.PRE_TYPE; } @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); // a filter has already forwarded // a filter has already determined serviceId return !ctx.containsKey(FilterConstants.FORWARD_TO_KEY) && !ctx.containsKey(FilterConstants.SERVICE_ID_KEY); } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); String token = request.getParameter("accessToken"); if(StringUtils.isBlank(token)) { log.warn("access token is empty"); // 过滤该请求,不对其进行路由 ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } log.info("access token ok"); return null; } }
实现自定义过滤器后,把它添加到 Spring 的 Beans 中
@SpringBootApplication @EnableZuulProxy public class ZuulFiltersApplication { public static void main(String[] args) { SpringApplication.run(ZuulFiltersApplication.class, args); } @Bean public AccessFilter accessFilter() { return new AccessFilter(); } }
下面咱们来测试下结果,启动项目 eureka-server
、product-serivce
(做为代理的服务)、zuul-filters
访问 http://localhost:8090/product/product/1
返回 401
访问 http://localhost:8090/product/product/1?accessToken=111
会正常路由到 product-service 的 /product/1
参考代码见:demo
@EnableZuulProxy
注解配合 Spring Boot Actuator,Zuul 会暴露额外的两个管理端点:Routes
和 Filters
。分别是关于路由和过滤器的端点(服务路由的端点在这里介绍 Spring Cloud Zuul 构建微服务网关)
spring-cloud-starter-netflix-zuul
已经依赖了 spring-boot-starter-actuator
,因此上面的工程已经包含了路由管理的功能。关于过滤器的管理端点的路径为 /filters
访问路径 http://localhost:8090/actuator/filters
{ "error": [ { "class": "org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter", "order": 0, "disabled": false, "static": true } ], "post": [ { "class": "org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter", "order": 1000, "disabled": false, "static": true } ], "pre": [ { "class": "org.springframework.cloud.netflix.zuul.filters.pre.DebugFilter", "order": 1, "disabled": false, "static": true }, { "class": "org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter", "order": -1, "disabled": false, "static": true }, { "class": "com.turbosnail.zuul.filter.AccessFilter", "order": 4, "disabled": false, "static": true }, { "class": "org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter", "order": -2, "disabled": false, "static": true }, { "class": "org.springframework.cloud.netflix.zuul.filters.pre.ServletDetectionFilter", "order": -3, "disabled": false, "static": true }, { "class": "org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter", "order": 5, "disabled": false, "static": true } ], "route": [ { "class": "org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter", "order": 100, "disabled": false, "static": true }, { "class": "org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter", "order": 10, "disabled": false, "static": true }, { "class": "org.springframework.cloud.netflix.zuul.filters.route.SendForwardFilter", "order": 500, "disabled": false, "static": true } ] }
访问404是由于没有暴露端点,能够设置 management.endpoints.web.exposure.include: '*'