API网关spring cloud gateway和负载均衡框架ribbon实战文章中,主要实现网关与负载均衡等基本功能,详见代码。本节内容将继续围绕此代码展开,主要讲解spring cloud gateway自定义过滤器的功能。本节内容的代码也会提交到GitHub上,注意提交的内容。

本节主要讲解全局过滤器和局部过滤器。注意下面的示例不能作为生产环境的代码,只是简单的演示自定义过滤器的使用方式,无需纠结实现的功能是否完善。下面主要针对不同的过滤器选择几种场景进行代码演示,不代表某个场景就必须使用全局或者局部的过滤器。

  • 全局过滤器:

1、限流:每分钟只能访问5次服务

2、接口用时统计

  • 局部过滤器:

1、简单的权限检查

2、指定IP访问

1、全局过滤器-限流


本节主要演示全局过滤器的用法:实现 GlobalFilter 和 Ordered,重写相关方法,加入到spring容器管理即可,无需配置,全局过滤器对所有的路由都有效。

package com.yefengyu.gateway.globalFilter;

import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;
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; import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; //全局过滤器,实现GlobalFilter接口,和Ordered接口即可。
@Component
public class FluidControlGlobalGatewayFilter implements GlobalFilter, Ordered
{
int capacity = 5;//桶的最大容量,即能装载 Token 的最大数量 int refillTokens = 1; //每次 Token 补充量 Duration duration = Duration.ofSeconds(1); //补充 Token 的时间间隔 private static final Map<String, Bucket> BUCKET_CACHE = new ConcurrentHashMap<>(); private Bucket createNewBucket()
{
Refill refill = Refill.greedy(refillTokens, duration);
Bandwidth limit = Bandwidth.classic(capacity, refill);
return Bucket4j.builder().addLimit(limit).build();
} @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
Bucket bucket = BUCKET_CACHE.computeIfAbsent(ip, k -> createNewBucket()); System.out.println("IP: " + ip + ",has Tokens: " + bucket.getAvailableTokens());
if (bucket.tryConsume(1))
{
return chain.filter(exchange);
}
else
{
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
} @Override
public int getOrder()
{
return -1000;
}
}

注意在pom.xml文件中加入依赖

<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>4.4.1</version>
</dependency>

2、全局过滤器-统计请求耗时


只需要在亲请求处理之前和之后标记时间即可。注意此处演示的是使用配置类的形式:

package com.yefengyu.gateway.globalFilter;

import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono; //全局过滤器,使用配置类形式,直接构造bean,使用注解完成Ordered接口功能,统计接口调用时间
@Configuration
public class GlobalGatewayFilterConfig
{
@Bean
@Order(-100)
public GlobalFilter elapsedGlobalFilter()
{
return (exchange, chain) -> {
//调用请求之前统计时间
Long startTime = System.currentTimeMillis();
return chain.filter(exchange).then().then(Mono.fromRunnable(() -> {
//调用请求之后统计时间
Long endTime = System.currentTimeMillis();
System.out.println(
exchange.getRequest().getURI().getRawPath() + ", cost time : " + (endTime - startTime) + "ms");
}));
};
}
}

3、局部过滤器-简单的权限检查


权限检查一般把信息存储在某处,请求到来之后进行核对,有权限的请求将真正执行。

1、首先编写一个工具类,对权限做管理。

package com.yefengyu.gateway.utitls;

import java.util.HashMap;
import java.util.Map; public final class AuthUtil
{
private static Map<String, String> map = new HashMap<>(); private AuthUtil()
{
} //程序启动的时候加载权限的信息,比如从文件、数据库中加载
public static void init()
{
map.put("tom", "123456");
} //简单判断
public static boolean isPermitted(String name, String password)
{
return map.containsKey(name) && map.get(name).equals(password);
} }

我们简单的将权限信息放到map中保管,init方法是初始化方法,isPermitted是对外提供一个判断是否有权限的方法。

2、服务启动的时候,需要初始化权限map,因此主启动类进行了修改:

package com.yefengyu.gateway;

import com.yefengyu.gateway.utitls.AuthUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; @SpringBootApplication
public class GatewayApplication
{
public static void main(String[] args)
{
SpringApplication springApplication = new SpringApplication(GatewayApplication.class);
springApplication.addListeners(new ApplicationListenerStarted());//增加监听器
springApplication.run(args);
} private static class ApplicationListenerStarted
implements ApplicationListener<ApplicationStartedEvent>
{
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent)
{
//权限初始化数据
AuthUtil.init();
}
}
}

3、编写一个局部过滤器,需要实现GatewayFilter, Ordered,实现相关的方法

package com.yefengyu.gateway.localFilter;

import com.yefengyu.gateway.utitls.AuthUtil;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; public class AuthGatewayFilter implements GatewayFilter, Ordered
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
//获取header的参数
String name = exchange.getRequest().getHeaders().getFirst("name");
String password = exchange.getRequest().getHeaders().getFirst("password");
boolean permitted = AuthUtil.isPermitted(name, password);//权限比较
if (permitted)
{
return chain.filter(exchange);
}
else
{
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
} @Override
public int getOrder()
{
return 10;
}
}

4、接着需要把上面自定义的局部过滤器加入到过滤器工厂,并且注册到spring容器中。

package com.yefengyu.gateway.localFilter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component; @Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<Object>
{
@Override
public GatewayFilter apply(Object config)
{
return new AuthGatewayFilter();
}
}

5、在配置文件中进行配置,如果不配置则不启用此过滤器规则。

4、局部过滤器-指定IP访问


我们的需求是如果在配置文件配置了一个IP,那么该ip就可以访问,其它IP通通不能访问。如果不使用该过滤器,那么所有IP都可以访问服务。

这里我们看到上面的AuthGatewayFilter和AuthGatewayFilterFactory代码本来就是为了同一个过滤器规则编写的两个类,如果过滤器规则很多,那么类文件就很多,其实这两个类可以合并,并且还会提供其它的功能:

package com.yefengyu.gateway.localFilter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; @Component
@Order(99)
public class IPForbidGatewayFilterFactory
extends AbstractGatewayFilterFactory<IPForbidGatewayFilterFactory.Config>
{ public IPForbidGatewayFilterFactory()
{
super(Config.class);
} @Override
public List<String> shortcutFieldOrder()
{
return Arrays.asList("forbidIp");
} @Override
public GatewayFilter apply(Config config)
{
return (exchange, chain) -> {
String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
if (config.getForbidIp().equals(ip))
{
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete(); };
} static public class Config
{
private String forbidIp; public String getForbidIp()
{
return forbidIp;
} public void setForbidIp(String forbidIp)
{
this.forbidIp = forbidIp;
}
}
}

Config类定义了一个属性,要重写List<String> shortcutFieldOrder()这个方法指定属性名称。规则逻辑很简单,判断配置文件中的ip是和请求来的ip是否相同,相同则可以访问服务。

配置文件:

正常测试

5、总结


全局过滤器,对所有的路由都有效,所有不用在配置文件中配置,主要实现了GlobalFilter 和 Ordered接口,并将过滤器注册到spring 容器。由于使用java配置类的方式也可以注册bean,所有也可以使用配置类的方式,Ordered接口使用Order注解代替,GlobalFilter 只是个接口,可以使用Lambda表达式替换。

局部过滤器,需要在配置文件中配置,如果配置,则该过滤器才会生效。主要实现GatewayFilter, Ordered接口,并通过AbstractGatewayFilterFactory的子类注册到spring容器中,当然也可以直接继承AbstractGatewayFilterFactory,在里面写过滤器逻辑,还可以从配置文件中读取外部数据。

本节代码已提交:github本次提交代码内容

spring cloud gateway自定义过滤器的更多相关文章

  1. Spring Cloud Alibaba学习笔记(19) - Spring Cloud Gateway 自定义过滤器工厂

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的内置过滤器工厂,若Spring Cloud Gateway内置的过滤器工厂无法满足我们的业务需求,那么此时就需要自定义自己的过 ...

  2. Spring Cloud Gateway自定义过滤器实战(观测断路器状态变化)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. Spring Cloud Alibaba学习笔记(21) - Spring Cloud Gateway 自定义全局过滤器

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的全局过滤器,本文介绍如何自定义全局过滤器. 自定义全局过滤需要实现GlobalFilter 接口,该接口和 GatewayFi ...

  4. spring cloud gateway 自定义GatewayFilterFactory

    spring cloud gateway提供了很多内置的过滤器,那么因为需求的关系,需要自定义实现,并且要可配置,在一番折腾之后,总算是解决了,那么久记录下来 对于自定义的factory,我们可以选择 ...

  5. Spring Cloud Alibaba学习笔记(17) - Spring Cloud Gateway 自定义路由谓词工厂

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,我们可以自定义路由谓词工厂来实现特定的需求. 例如有某个服务 ...

  6. Spring cloud gateway自定义filter以及负载均衡

    自定义全局filter package com.example.demo; import java.nio.charset.StandardCharsets; import org.apache.co ...

  7. spring cloud gateway 全局过滤器

    全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等. 接口定义类:org.springframework.cloud.gateway ...

  8. Spring Cloud Gateway自定义异常处理Exception Handler

    版本: Spring Cloud 2020.0.3 常见的方法有 实现自己的 DefaultErrorWebExceptionHandler 或 仅实现ErrorAttributes. 方法1: Er ...

  9. 微服务网关实战——Spring Cloud Gateway

    导读 作为Netflix Zuul的替代者,Spring Cloud Gateway是一款非常实用的微服务网关,在Spring Cloud微服务架构体系中发挥非常大的作用.本文对Spring Clou ...

随机推荐

  1. oracle 创建表的规则

    1.表明首字母 应该为字母 2.表名的最大长度为30个字符 3.不能使用oracle保留字和关键字来作表名 4.同一用户下的不同表不能具有相同的名称 5.可以使用下划线.数字和字母,但不能使用空格与单 ...

  2. 安装caffe碰到的坑(各种.so未找到)

    ./include/caffe/common.hpp:4:32: fatal error: boost/shared_ptr.hpp: 没有那个文件或目录 所有类似于上面的错误,都可以用如下格式来解决 ...

  3. 几道好玩的JS题目,看看你能答中几道。

    1,for (var i = 0; i < 5; i++) { console.log(i); } 这几行代码会输出什么? 输出 0 1 2 3 4 2,for (var i = 0; i &l ...

  4. 苹果CMSv10对接微信公众号教程

    首先声明下,对接公众号的话需要自行注册公众号“订阅号”  对接失败的原因大多是域名变红导致!简单的测试方法就是把域名链接发给qq好友或是qq群里看看有没有变红 域名变红以后大概率不会对接成功的,请知悉 ...

  5. 洛谷P3948 数据结构——题解

    题目传送 感觉这道题秀了我一地的智商... 审题没审好,没确定带修改的操作中询问的次数<=1000,且max和min都是事先给好.不变的.想了半天线段树.分块,却忘了最基础的暴力. 写不出题时先 ...

  6. ModelSerializer 使用知识点_序列化和反序列化用法区别

    1.ModelSerializer  如下 from api_test.errorCode.errorCode import Statusclass RelatedbSerializer(serial ...

  7. python3学习笔记(三):注释和字符串

    一.注释 为了让别人能够更容易理解程序,使用注释是非常有效的,即使是自己回头再看旧代码也是一样. # 打印圆的周长: print(2* pi* r) 在python 中用井号(#)表示注释.井号(#) ...

  8. VMware NAT模式下,公司代理下上网 续

    这是在非公司环境下,VMware NAT 静态IP模式下上网 由于公司的限制,可能需要通过proxy上网. 下面是我在公司的配置(参考其他) 1. 修改 /etc/environment http_p ...

  9. AtCoder AGC001F Wide Swap (线段树、拓扑排序)

    题目链接: https://atcoder.jp/contests/agc001/tasks/agc001_f 题解: 先变成排列的逆,要求\(1\)的位置最小,其次\(2\)的位置最小,依次排下去( ...

  10. Spring Cloud架构教程 (一)Hystrix监控面板

    下面我们基于之前的示例来结合Hystrix Dashboard实现Hystrix指标数据的可视化面板,这里我们将用到下之前实现的几个应用,包括: eureka-server:服务注册中心 eureka ...