这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战。
上篇文章我们自定义了一个局部过滤器,用来校验Http请求头部中的Token,本篇文章我们将自定义一个全局过滤器,用来缓存HTTP请求某一类型的body。
自定义全局过滤器
之所以要自定义一个全局过滤器缓存HTTP请求某一类型的body的原因是因为我们将来会通过我们的网关去访问鉴权微服务,也就是e-commerce-authority-center。我们的SpringCloud Gateway是基于Spring WebFlux实现的,Spring WebFlux是由Spring框架5.0中引入的新的响应式的Web框架,它与SpringMVC不同。
通过authority我们可以去完成登录、注册、获取Token的过程,这些过程也会去通过网关里的Filter去实现。但是要登录到系统需要提供用户名密码,而且这个请求需要是POST请求,但是我们在Filter里是拿不到POST请求的数据的,所以我们需要有一个过滤器将用户的请求的数据缓存下来,这样的话,在我们另一个过滤器里去实现登录注册的时候才能拿到请求的数据,所以这篇文章里实现的过滤器是为之后的过滤器做准备。
1、创建对应的包和类
在e-commerce-gateway子模块下的com.sheep.ecommerce.filter包下创建一个类GlobalCacheRequestBodyFilter,即用来缓存HTTP请求某一类型的body的全局过滤器。
再在com.sheep.ecommerce包下创建一个constant包,包下创建GatewayConstant,存储网关常量。
2、编写 GatewayConstant
/**
* <h1>网关常量定义</h1>
* */
public class GatewayConstant {
/** 登录 uri */
public static final String LOGIN_URI = "/e-commerce/login";
/** 注册 uri */
public static final String REGISTER_URI = "/e-commerce/register";
/** 去授权中心拿到登录 token 的 uri 格式化接口 */
public static final String AUTHORITY_CENTER_TOKEN_URL_FORMAT =
"http://%s:%s/ecommerce-authority-center/authority/token";
/** 去授权中心注册并拿到 token 的 uri 格式化接口 */
public static final String AUTHORITY_CENTER_REGISTER_URL_FORMAT =
"http://%s:%s/ecommerce-authority-center/authority/register";
}
3、编写全局过滤器 GlobalCacheRequestBodyFilter
因为这是一个全局过滤器,所以需要去实现GlobalFilter和Ordered接口,记得要打上@Component注解。
代码如下:
/**
* <h1>缓存请求 body 的全局过滤器</h1>
* Spring WebFlux
* */
@Slf4j
@Component
@SuppressWarnings("all")
public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean isloginOrRegister =
exchange.getRequest().getURI().getPath().contains(GatewayConstant.LOGIN_URI)
|| exchange.getRequest().getURI().getPath().contains(GatewayConstant.REGISTER_URI);
if (null == exchange.getRequest().getHeaders().getContentType()
|| !isloginOrRegister) {
return chain.filter(exchange);
}
// DataBufferUtils.join 拿到请求中的数据 --> DataBuffer
return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
// 确保数据缓冲区不被释放, 必须要 DataBufferUtils.retain
DataBufferUtils.retain(dataBuffer);
// defer、just 都是去创建数据源, 得到当前数据的副本
Flux<DataBuffer> cachedFlux = Flux.defer(() ->
Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
// 重新包装 ServerHttpRequest, 重写 getBody 方法, 能够返回请求数据
ServerHttpRequest mutatedRequest =
new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
// 将包装之后的 ServerHttpRequest 向下继续传递
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 1;
}
}
相应注释也在代码之中了,这边就不再重复描述了。