Spring Cloud Gateway通过全局过滤器实现接口防刷

开发 前端
通过某种机制实现对系统中的某些接口在规定的时间段内只能让某个具体的客户端访问指定次数,超出次数,就不让访问了。

环境:Spring Boot2.7.12 + Spring Cloud2021.0.7

1 概念

通过某种机制实现对系统中的某些接口在规定的时间段内只能让某个具体的客户端访问指定次数,超出次数,就不让访问了。等待指定的时间到期后又能继续访问接口;这里需要注意的是是控制到每一个具体的接口上,所以必须确定两个要素:

  1. 客户端是谁
  2. 访问的接口

2 实现原理

可以通过2种方式实现:

  1. 通过网关
    可以控制到所有的服务
  2. 通过AOP
    该方案只能针对具体的每一个服务,代码重复,如果通过

本篇文章我们通过网关实现,那接下来就是考虑上该如何去记录当前客户端访问的具体接口在指定的时间内已经访问了多少次了?通过两种方式:

  1. JVM层
    该种实现方式,你需要自己实现时效性的检查,实现麻烦
  2. 通过Redis
    Redis本身就可以对Key设置时效性,所以非常的方便。本文通过Redis实现。

通过 Redis 记录访问请求的次数,每次访问都进行递减,如果次数小于0就返回错误信息,当到了指定的时效则Redis会对过期的key进行自动删除。

3 代码实现

Redis配置

spring:
  redis:
    host: localhost
    port: 6379
    password: 123123
    database: 8
    lettuce:
      pool:
        maxActive: 8
        maxIdle: 100
        minIdle: 10
        maxWait: -1

定义全局过滤器

@Component
public class BrushProofFilter implements GlobalFilter, Ordered {


  private final ReactiveStringRedisTemplate reactiveStringRedisTemplate ;
  
  public BrushProofFilter(ReactiveStringRedisTemplate reactiveStringRedisTemplate) {
    this.reactiveStringRedisTemplate = reactiveStringRedisTemplate ;
  }
  
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 获取客户端的请求ip
    InetAddress address = exchange.getRequest().getRemoteAddress().getAddress();
    // 获取请求的URI
    String path = exchange.getRequest().getPath().toString() ;
    // 将其组合为Redis中的Key
    String key = ("ratelimiter:" + address + ":" + path) ;
    // 通过抛出异常的方式终止序列,然后通过自定义的WebExceptionHandler处理异常信息 
    return this.reactiveStringRedisTemplate.opsForValue()
        // 这里固定设置每30s,访问10次
        .setIfAbsent(key, "10", Duration.ofSeconds(30))
        .flatMap(exist -> {
          return this.reactiveStringRedisTemplate.opsForValue().decrement(key) ;
        })
        .doOnNext(num -> {
          if (num < 0) {
            throw new BrushProofException("你访问太快了") ;
          }
        })
        .then(chain.filter(exchange)) ;
  }


  @Override
  public int getOrder() {
    return -2 ;
  }


}

自定义异常

public class BrushProofException extends RuntimeException {


  private static final long serialVersionUID = 1L;
  
  public BrushProofException(String message) {
    super(message) ;
  }
  
}

自定义异常处理句柄

@Component
public class RatelimiterWebExceptionHandler implements WebExceptionHandler {


  @Override
  public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    if (ex instanceof RatelimiterException re) {
      ServerHttpResponse response = exchange.getResponse() ;
      response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR) ;
      response.getHeaders().add("Content-Type", "text/html;charset=utf8") ;
      // 直接输出了,异常不进行传递
      return response.writeWith(Mono.just(response.bufferFactory().wrap(("你访问太快了").getBytes()))) ;
    }
    // 如果是其它异常,则将异常继续向下传递
    return Mono.error(ex) ;
  }


}

访问测试

图片图片

因为我这里没有这个接口,所以返回的是降级接口,也算是正常

当超过10次后:

Redis

图片图片

以客户端请求ip + path作为key

图片图片


责任编辑:武晓燕 来源: 实战案例锦集
相关推荐

2023-01-26 01:41:27

核心全局过滤器

2023-04-14 09:01:25

2021-01-14 08:13:39

Spring Clou应用内置过滤器

2024-04-03 08:08:15

谓词网关开发

2017-04-12 14:43:01

Spring ClouZuul过滤器

2017-05-04 22:30:17

Zuul过滤器微服务

2023-02-15 08:12:19

http超时过滤器

2024-01-05 09:04:35

隆过滤器数据结构哈希函数

2017-09-15 23:29:53

Spring Clou微服务架构过滤器

2021-07-05 15:22:03

Servlet过滤器客户端

2022-02-16 23:58:41

Spring过滤器验证码

2009-07-08 15:30:56

Servlet过滤器

2009-09-29 13:55:23

Hibernate设置

2009-07-08 16:07:04

Servlet过滤器配

2011-06-29 16:14:59

Qt 事件 过滤器

2009-07-14 09:09:08

Swing模型过滤器

2023-02-20 10:13:00

灰度发布实现

2022-02-21 23:58:49

Spring过滤器顺序值

2009-06-18 10:13:00

Hibernate过滤

2009-07-08 17:33:37

Servlet过滤器
点赞
收藏

51CTO技术栈公众号