Spring Cloud / Alibaba 微服务架构

这是我参与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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
arduino复制代码/**
* <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注解。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
less复制代码/**
* <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;
}
}

相应注释也在代码之中了,这边就不再重复描述了。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%