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

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

2、接口用时统计 局部过滤器: 1、简单的权限检查

2、指定IP访问

1、全局过滤器-限流

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

packagecom.yefengyu.gateway.globalFilter;importio.github.bucket4j.Bandwidth;importio.github.bucket4j.Bucket;importio.github.bucket4j.Bucket4j;importio.github.bucket4j.Refill;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.Ordered;importorg.springframework.http.HttpStatus;importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;importjava.time.Duration;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;//全局过滤器,实现GlobalFilter接口,和Ordered接口即可。

@Componentpublic class FluidControlGlobalGatewayFilter implementsGlobalFilter, Ordered

{int capacity = 5;//桶的最大容量,即能装载 Token 的最大数量

int refillTokens = 1; //每次 Token 补充量

Duration duration= Duration.ofSeconds(1); //补充 Token 的时间间隔

private static final Map BUCKET_CACHE = new ConcurrentHashMap<>();privateBucket createNewBucket()

{

Refill refill=Refill.greedy(refillTokens, duration);

Bandwidth limit=Bandwidth.classic(capacity, refill);returnBucket4j.builder().addLimit(limit).build();

}

@Overridepublic Monofilter(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))

{returnchain.filter(exchange);

}else{

exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);returnexchange.getResponse().setComplete();

}

}

@Overridepublic intgetOrder()

{return -1000;

}

}

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

com.github.vladimir-bukhtoyarov

bucket4j-core

4.4.1

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

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

packagecom.yefengyu.gateway.globalFilter;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.annotation.Order;importreactor.core.publisher.Mono;//全局过滤器,使用配置类形式,直接构造bean,使用注解完成Ordered接口功能,统计接口调用时间

@Configurationpublic classGlobalGatewayFilterConfig

{

@Bean

@Order(-100)publicGlobalFilter 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、首先编写一个工具类,对权限做管理。

packagecom.yefengyu.gateway.utitls;importjava.util.HashMap;importjava.util.Map;public final classAuthUtil

{private static Map map = new HashMap<>();privateAuthUtil()

{

}//程序启动的时候加载权限的信息,比如从文件、数据库中加载

public static voidinit()

{

map.put("tom", "123456");

}//简单判断

public static booleanisPermitted(String name, String password)

{return map.containsKey(name) &&map.get(name).equals(password);

}

}

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

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

packagecom.yefengyu.gateway;importcom.yefengyu.gateway.utitls.AuthUtil;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.context.event.ApplicationStartedEvent;importorg.springframework.context.ApplicationListener;

@SpringBootApplicationpublic classGatewayApplication

{public static voidmain(String[] args)

{

SpringApplication springApplication= new SpringApplication(GatewayApplication.class);

springApplication.addListeners(new ApplicationListenerStarted());//增加监听器

springApplication.run(args);

}private static classApplicationListenerStartedimplements ApplicationListener{

@Overridepublic voidonApplicationEvent(ApplicationStartedEvent applicationStartedEvent)

{//权限初始化数据

AuthUtil.init();

}

}

}

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

packagecom.yefengyu.gateway.localFilter;importcom.yefengyu.gateway.utitls.AuthUtil;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.core.Ordered;importorg.springframework.http.HttpStatus;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;public class AuthGatewayFilter implementsGatewayFilter, Ordered

{

@Overridepublic Monofilter(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)

{returnchain.filter(exchange);

}else{

exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);returnexchange.getResponse().setComplete();

}

}

@Overridepublic intgetOrder()

{return 10;

}

}

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

packagecom.yefengyu.gateway.localFilter;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;importorg.springframework.stereotype.Component;

@Componentpublic class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory{

@OverridepublicGatewayFilter apply(Object config)

{return newAuthGatewayFilter();

}

}

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

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

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

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

packagecom.yefengyu.gateway.localFilter;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;importorg.springframework.core.annotation.Order;importorg.springframework.http.HttpStatus;importorg.springframework.stereotype.Component;importjava.util.Arrays;importjava.util.List;

@Component

@Order(99)public classIPForbidGatewayFilterFactoryextends AbstractGatewayFilterFactory{publicIPForbidGatewayFilterFactory()

{super(Config.class);

}

@Overridepublic ListshortcutFieldOrder()

{return Arrays.asList("forbidIp");

}

@OverridepublicGatewayFilter apply(Config config)

{return (exchange, chain) ->{

String ip=exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();if(config.getForbidIp().equals(ip))

{returnchain.filter(exchange);

}

exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);returnexchange.getResponse().setComplete();

};

}static public classConfig

{privateString forbidIp;publicString getForbidIp()

{returnforbidIp;

}public voidsetForbidIp(String forbidIp)

{this.forbidIp =forbidIp;

}

}

}

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

配置文件:

正常测试

5、总结

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

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

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐