spring cloud gateway自定义过滤器

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本次提交代码内容

原文地址:https://www.cnblogs.com/ye-feng-yu/p/11111803.html

时间: 2024-10-28 03:02:45

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

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

自定义全局filter package com.example.demo; import java.nio.charset.StandardCharsets; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframe

spring cloud gateway获取response body

网关发起请求后,微服务返回的response的值要经过网关才发给客户端.本文主要讲解在spring cloud gateway 的过滤器中获取微服务的返回值,因为很多情况我们需要对这个返回进行处理.网上有很多例子,但是都没有解决我的实际问题,最后研究了下源码找到了解决方案. 本节内容主要从如下几个方面讲解,首先需要了解我的博文的内容:API网关spring cloud gateway和负载均衡框架ribbon实战 和 spring cloud gateway自定义过滤器 本文也将根据上面两个项目

spring cloud 2.x版本 Gateway自定义过滤器教程

前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server.eureka-client.eureka-ribbon.eureka-feign和spring-gataway的实现. 参考 eureka-server eureka-client eureka-ribbon eureka-feign spring-gateway 概术 Spring Cloud Gateway内部已经提供非常多的过滤器f

Spring Cloud Gateway 内置的过滤器工厂

本文基于Spring Cloud Greenwich SR2 [TOC] 内置的过滤器工厂 这里简单将Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格,虽然不是很详细,但能作为速览使用.如下: 过滤器工厂 作用 参数 AddRequestHeader 为原始请求添加Header Header的名称及值 AddRequestParameter 为原始请求添加请求参数 参数名称及值 AddResponseHeader 为原始响应添加Header Header的名称及值 D

Spring Cloud gateway 网关服务二 断言、过滤器

微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring Cloud alibaba只需要你学会一个就会拥有俩种微服务治理框架技术.何乐而不为呢?加油吧!骚猿年 上一篇我们讲述了gateway 的路由功能其实也类似与zuul服务的路由转发. 今天主要讲一下断言机制. 内置的断言工厂 介绍 Spring Cloud Gateway将路由作为Spring Web

Spring Cloud Gateway服务网关

原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性. Spring Clou

API网关spring cloud gateway和负载均衡框架ribbon实战

通常我们如果有一个服务,会部署到多台服务器上,这些微服务如果都暴露给客户,是非常难以管理的,我们系统需要有一个唯一的出口,API网关是一个服务,是系统的唯一出口.API网关封装了系统内部的微服务,为客户端提供一个定制的API.客户端只需要调用网关接口,就可以调用到实际的微服务,实际的服务对客户不可见,并且容易扩展服务. API网关可以结合ribbon完成负载均衡的功能,可以自动检查微服务的状况,及时剔除或者加入某个微服务到可用服务列表.此外网关可以完成权限检查.限流.统计等功能.下面我们将一一完

跟我学SpringCloud | 第十二篇:Spring Cloud Gateway初探

SpringCloud系列教程 | 第十二篇:Spring Cloud Gateway初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如无特殊说明,本系列教程全采用以上版本 前面我们在聊服务网关Zuul的时候提到了Gateway,那么Zuul和Gateway都是服务网关,这两个有什么区别呢? 1. Zuul和Gateway的恩怨情仇 1.1 背景 Zuul是Netflix开源的一个项目,Spring只是将Zuul集成在了Spring

第二代微服务网关组件 - Spring Cloud Gateway

[TOC] 初识Spring Cloud Gateway 简介: Spring Cloud Gateway是Spring Cloud体系的第二代网关组件,基于Spring 5.0的新特性WebFlux进行开发,底层网络通信框架使用的是Netty,所以其吞吐量高.性能强劲,未来将会取代第一代的网关组件Zuul.Spring Cloud Gateway可以通过服务发现组件自动转发请求,默认集成了Ribbon做负载均衡,以及默认使用Hystrix对网关进行保护,当然也可以选择其他的容错组件,例如Sen