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

定义全局过滤器

@Componentpublic 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) ;  }  }

自定义异常处理句

@Componentpublic class RatelimiterWebExceptionHandler implements WebExceptionHandler {
  @Override  public Mono 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

超过10次后,限制访问

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=14649,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?