项目Spring Cloud Gateway入门 - 网关过滤器工厂
一、全局过滤器GlobalFilter
全局过滤器是针对于网关而言通用的功能组件实现,采用全局来实现。
以下是两个如何全局过滤器GlobalFilter的实例
1.1 实例1:定义ForwardAuthFilter
@Component
public class ForwardAuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest newRequest = exchange
.getRequest()
.mutate()
// 为请求追加 Id-Token 参数
.header(SaIdUtil.ID_TOKEN, SaIdUtil.getToken())
.build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
}
}
1.2 实例2:定义ForwardAuthFilter
@Component
@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true")
public class XssFilter implements GlobalFilter, Ordered {
// 跨站脚本的 xss 配置,nacos自行添加
@Autowired
private XssProperties xss;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// GET DELETE 不过滤
HttpMethod method = request.getMethod();
if (method == null || method.matches("GET") || method.matches("DELETE")) {
return chain.filter(exchange);
}
// 非json类型,不过滤
if (!isJsonRequest(exchange)) {
return chain.filter(exchange);
}
// excludeUrls 不过滤
String url = request.getURI().getPath();
if (StringUtils.matches(url, xss.getExcludeUrls())) {
return chain.filter(exchange);
}
ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange);
return chain.filter(exchange.mutate().request(httpRequestDecorator).build());
}
private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange) {
ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
Flux<DataBuffer> body = super.getBody();
return body.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
String bodyStr = new String(content, StandardCharsets.UTF_8);
// 防xss攻击过滤
bodyStr = HtmlUtil.cleanHtmlTag(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes();
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
});
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
// 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length
httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
};
return serverHttpRequestDecorator;
}
/**
* 是否是Json请求
*
* @param exchange HTTP请求
*/
public boolean isJsonRequest(ServerWebExchange exchange) {
String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
}
@Override
public int getOrder() {
return -100;
}
}
二、局部过滤器AbstractGatewayFilterFactory
内部类写法,是为了指定过滤器的优先级,要优先于全局过滤器,否则容易造成全局过滤器GlobalFilter 拦截到指定 局部过滤器AbstractGatewayFilterFactory 的配置内容,从而导致局部过滤器AbstractGatewayFilterFactory 失效,以下是两个如何局部过滤器AbstractGatewayFilterFactory的实例
2.1 实例1:BlackListUrlFilter 黑名单
@Component
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String url = exchange.getRequest().getURI().getPath();
if (config.matchBlacklist(url)) {
return WebFluxUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
}
return chain.filter(exchange);
};
}
public BlackListUrlFilter() {
super(Config.class);
}
public static class Config {
private List<String> blacklistUrl;
private List<Pattern> blacklistUrlPattern = new ArrayList<>();
public boolean matchBlacklist(String url) {
return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
}
public List<String> getBlacklistUrl() {
return blacklistUrl;
}
public void setBlacklistUrl(List<String> blacklistUrl) {
this.blacklistUrl = blacklistUrl;
this.blacklistUrlPattern.clear();
this.blacklistUrl.forEach(url -> {
this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
});
}
}
}
2.2 实例2:CacheRequestFilter
获取body请求数据(解决流不能重复读取问题)
@Component
public class CacheRequestFilter extends AbstractGatewayFilterFactory<CacheRequestFilter.Config> {
public CacheRequestFilter() {
super(Config.class);
}
@Override
public String name() {
return "CacheRequestFilter";
}
@Override
public GatewayFilter apply(Config config) {
CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter();
Integer order = config.getOrder();
if (order == null) {
return cacheRequestGatewayFilter;
}
return new OrderedGatewayFilter(cacheRequestGatewayFilter, order);
}
public static class CacheRequestGatewayFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// GET DELETE 不过滤
HttpMethod method = exchange.getRequest().getMethod();
if (method == null || method.matches("GET") || method.matches("DELETE")) {
return chain.filter(exchange);
}
return DataBufferUtils.join(exchange.getRequest().getBody()).map(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
return bytes;
}).defaultIfEmpty(new byte[0]).flatMap(bytes -> {
DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
if (bytes.length > 0) {
return Flux.just(dataBufferFactory.wrap(bytes));
}
return Flux.empty();
}
};
return chain.filter(exchange.mutate().request(decorator).build());
});
}
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("order");
}
static class Config {
private Integer order;
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
this.order = order;
}
}
}
三、GatewayFilterFactory介绍
对于有些非通用的功能或者面向用户自定义的功能采用全局过滤器实现显然是不合理的,基于此出发开源作者变换了思路,采用工厂模式来即时生产一个自定义或者配置的过滤器来过滤当前的请求。
3.1 数量众多的内置GatewayFilter工厂
Spring Cloud Gateway包含许多内置的GatewayFilter工厂。
内置的过滤器工厂一共有22个
3.2 内置GatewayFilter工厂所处位置
分别位于
- org.springframework.cloud.gateway.filter.factory包中
- org.springframework.cloud.gateway.filter.factory.rewrite包中
3.3 内置GatewayFilter工厂实现
网关过滤器工厂接口有多个实现类,在每个 GatewayFilterFactory 实现类的 apply( T config) 方法里,都声明了一个实现 GatewayFilter 的内部类。
3.4 内置GatewayFilter工厂实现 - AddRequestHeaderGatewayFilterFactory
public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.add(config.getName(), value)).build();
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public String toString() {
return filterToStringCreator(AddRequestHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}
}
3.5 其他内置GatewayFilter工厂实现
其实核心逻辑区别在filter上,那就只聚焦在filter 的实现上看。
3.5.1 AddRequestParameterGatewayFilterFactory
URI uri = exchange.getRequest().getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
// TODO urlencode?
query.append(config.getName());
query.append('=');
query.append(value);
try {
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
}
catch (RuntimeException ex) {
throw new IllegalStateException("Invalid URI query: \"" query.toString() "\"");
}
3.5.2 AddResponseHeaderGatewayFilterFactory
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
exchange.getResponse().getHeaders().add(config.getName(), value);
return chain.filter(exchange);
3.5.3 CacheRequestBodyGatewayFilterFactory
ServerHttpRequest request = exchange.getRequest();
URI requestUri = request.getURI();
String scheme = requestUri.getScheme();
// Record only http requests (including https)
if ((!"http".equals(scheme) && !"https".equals(scheme))) {
return chain.filter(exchange);
}
Object cachedBody = exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
if (cachedBody != null) {
return chain.filter(exchange);
}
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> {
final ServerRequest serverRequest = ServerRequest
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders);
return serverRequest.bodyToMono((config.getBodyClass())).doOnNext(objectValue -> {
exchange.getAttributes().put(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR, objectValue);
}).then(Mono.defer(() -> {
ServerHttpRequest cachedRequest = exchange
.getAttribute(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
Assert.notNull(cachedRequest, "cache request shouldn't be null");
exchange.getAttributes().remove(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
return chain.filter(exchange.mutate().request(cachedRequest).build());
}));
});
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgeahke
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01