概述:SpringCloud Gateway 除了路由功能之外与Zuul一样还有过滤器功能,用来对接口调用进行过滤处理。根据过滤器在调用接口的位置分为前置过滤器(pre)和后置过滤器(post)。根据处理的范围又分为全局过滤器(GlobalFilter)和局部过滤器(gatewayFilter)
一、相关概念
生命周期:
PRE(前置): 这种过滤器在请求路由之前被调用,我们可以利用其做统一鉴权,记录调用信息入参等功能。
POST(后置):这种过滤器在请求从微服务端返回后执行,我们用来统计收集调用信息,记录日志等功能,以及修改response中的属性信息。
作用范围分类:
局部过滤器(GatewayFilter):针对单个的服务路由,可以对访问的url过滤,也可以根据对请求参数进行拦截过滤。SpringCloud Gateway 内部实现了很多不同的局部过滤器,比如上一章节《SpringCloud 微服务网关Gateway 动态路由配置》中使用的 - RewritePath 则使用的为 RewritePathGatewayFilterFactory.class 类来实现其功能。其他的还有:
全局过滤器(GlobalFilter):作用与所有路由信息。用户可以自定义自己的GlobalFilter。全局过滤器通过用来实现统一鉴权,安全校验等功能。使用的场景比较多。SpringCloud内部也有很多已经实现全局过滤器的实现类。
二、使用全局过滤器实现统一鉴权
自定义全局过滤器需要实现GlobalFilter、Ordered 接口。在getOrdered方法中返回过滤器执行的优先级,在filter 方法中编写执行逻辑。
return chain.filter(exchange): 表示继续向后执行。
return exchange.getResponse().setComplete(): 表示拦截停止执行立即请求返回。
如下代码示例根据传参是否包含access-token参数作为是否登录的逻辑进行判断过滤:
package com.xiaohui.gateway.filter;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class LoginFilter implements GlobalFilter, Ordered {
/**
* 过滤器实现的业务逻辑。
* return chain.filter(exchange) 表示继续向下执行
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("执行了自定义的全局过滤器-----接口之前");
String token = exchange.getRequest().getQueryParams().getFirst("access-token");
if(StringUtils.isEmpty(token)){//未登录返回
System.out.println("没有登录....");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 指定全局过滤器的执行顺序,值越小 优先级越高
* @return
*/
@Override
public int getOrder() {
return 1;
}
}
三、局部过滤器实现计时功能
编写局部自定义过滤器com/xiaohui/gateway/filter/ElapsedFilter.java
package com.xiaohui.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class ElapsedFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put("timeBegin", System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute("timeBegin");
if (startTime != null) {
System.out.println(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
}
})
);
}
@Override
public int getOrder() {
return 2;
}
}
主启动类或其他配置类中注入RouteLocator 实现添加自定义路由信息。
package com.xiaohui.gateway;
import com.xiaohui.gateway.filter.ElapsedFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class GatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServerApplication.class,args);
}
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/path1/path2/**")
.filters(f -> f.stripPrefix(2)
.filter(new ElapsedFilter())
.addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
.uri("lb://cloud-payment-service")
.order(1)
.id("fluent_customer_service")
).build();
}
}
服务提供者接口服务名称:cloud-payment-service
Gateway 访问接口:http://127.0.0.1:8080/path1/path2/payment/get/2?access-token=111
第二部分和第三部分配置文件一样如下:
server:
port: 8080
spring:
application:
name: api-gateway-server #服务名称
cloud:
gateway:
routes:
#配置路由: 路由id,路由到微服务的uri,断言(判断条件)
- id: product-service #保持唯一
#uri: http://127.0.0.1:8001 #目标为服务地址
uri: lb://cloud-payment-service # lb:// 根据服务名称从注册中心获取请求地址路径
predicates:
#- Path=/payment/** #路由条件 path 路由匹配条件
- Path=/product-service/** #给服务名称前加上一个固定的应用分类路径 将该路径转发到 http://127.0.0.1:8001/payment/get/1
filters: #配置路由过滤器 http://127.0.0.1:8080/product-service/payment/get/1 -> http://127.0.0.1:8001/payment/get/1
- RewritePath=/product-service/(?<segment>.*),/$\{segment} #路径重写的过滤器,在yml中$写为 $\
# 配置自动根据微服务名称进行路由转发 http://127.0.0.1:8080/cloud-payment-service/payment/get/1
discovery:
locator:
enabled: true #开启根据服务名称自动转发
lower-case-service-id: true #微服务名称已小写形式呈现
#eureka 注册中心
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka1.com:9000/eureka/
instance:
prefer-ip-address: true #使用ip进行注册