SpringCloud + Redis 实现Api接口限流 防止恶意刷接口

一、API接口防刷

顾名思义,想让某个接口某个人在某段时间内只能请求N次。

二、原理

在请求的时候,服务器通过Redis记录你请求的次数,如果次数超过限制就不给访问。
在redis保存的key是有失效的,过期就会删除。

三、api限流的场景

限流的需求出现在许多常见的场景中:

  • 秒杀活动,有人使用软件恶意刷单抢货,需要限流防止机器参与活动
  • 某api被各式各样系统广泛调用,严重消耗网络、内存等资源,需要合理限流
  • 淘宝获取ip所在城市接口、微信公众号识别微信用户等开发接口,免费提供给用户时需要限流,更具有实时性和准确性的接口需要付费。

四、api限流代码实现

使用自定义注解的方式实现

1.添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>3.1.5</version>
</dependency>
<!--nacos 服务注册中心-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2021.1</version>
</dependency>
<!--nacos 配置中心-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2021.1</version>
</dependency>
<!--redis 依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.添加自定义AccessLimit注解

package com.example.demo.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义接口:api接口限流* @author qzz* @date 2024/1/11*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {/*** 时间间隔(单位秒)* @return*/int seconds() default 60;/*** 最大访问次数* @return*/int maxCount() default 60;/*** 是否需要登录* @return*/boolean needLogin() default true;
}

3.添加AccessLimitIntercepter拦截器

自定义一个拦截器,请求之前,进行请求次数校验

package com.example.demo.intercepter;import com.alibaba.fastjson.JSON;
import com.example.demo.aop.AccessLimit;
import com.example.demo.enums.CodeMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;/*** 限制访问拦截器* @author qzz* @date 2024/1/12*/
@Slf4j
@Component
public class AccessLimitIntercepter implements HandlerInterceptor {private RedisTemplate<String,Object> redisTemplate;@Autowiredpublic AccessLimitIntercepter(RedisTemplate<String,Object> redisTemplate){this.redisTemplate = redisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//方法请求拦截    Handler 是否为 HandlerMethod 实例if(handler instanceof HandlerMethod){HandlerMethod hm = (HandlerMethod) handler;// 获取方法Method method = hm.getMethod();// 是否有AccessLimit注解if (!method.isAnnotationPresent(AccessLimit.class)) {return true;}//获取方法中的AccessLimit注解AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);if(accessLimit == null){return true;}int seconds = accessLimit.seconds();int maxCount = accessLimit.maxCount();boolean needLogin = accessLimit.needLogin();String key=request.getRequestURI();//判断是否登录if(needLogin){//获取登录的session进行判断//...//获取用户idLong userId = 1L;key+=userId;}//从redis中获取用户访问的次数Object count = redisTemplate.opsForValue().get(key);log.info("count:{}",count);if(count == null){//第一次访问redisTemplate.opsForValue().set(key,1, Long.valueOf(String.valueOf(seconds)), TimeUnit.SECONDS);log.info("--------------------------第一次访问--------------------------");}else if(count!=null && Integer.valueOf(String.valueOf(count)) < maxCount){//次数自增redisTemplate.opsForValue().increment(key,1);log.info("--------------------------次数自增--------------------------");}else{//超出访问次数render(response, CodeMsg.ACCESS_LIMIT_REACHED);return false;}}return true;}private void render(HttpServletResponse response, CodeMsg codeMsg) throws IOException {response.setContentType("application/json;charset=UTF-8");OutputStream outputStream = response.getOutputStream();String str = JSON.toJSONString(codeMsg);outputStream.write(str.getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}

拦截器写好了,但是还得添加注册

4.WebConfig配置类

如果是Springboot的版本是2.*,只需要实现WebMvcConfigurer,然后重写addInterceptors()方法,添加自定义拦截器即可。

如果是Springboot的版本是1.*,只需要实现WebMvcConfigurerAdapter,然后重写addInterceptors()方法,添加自定义拦截器即可。

我的版本是2.4.6,所以代码如下:

package com.example.demo.config;import com.example.demo.intercepter.AccessLimitIntercepter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;/*** @author qzz* @date 2024/1/12*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate AccessLimitIntercepter accessLimitIntercepter;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//添加拦截器registry.addInterceptor(accessLimitIntercepter);}
}

5.controller控制层测试接口

使用方式:在方法上使用注解@AccessLimit(seconds = 5, maxCount = 5, needLogin = true)

默认5秒内,每个接口只能请求5次

package com.example.demo.controller;import com.example.demo.aop.AccessLimit;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 拦截器注册* @author qzz* @date 2024/1/11*/
@RestController
public class TestController {@AccessLimit(seconds = 5, maxCount = 5, needLogin = true)@RequestMapping("test/api/limit")public String testApiLimit(){return "test api limit";}
}

五、完整代码

代码点击此处下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/617413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPT-4:智能语言模型的新篇章

随着人工智能技术的飞速发展&#xff0c;智能语言模型已经成为了我们日常生活和工作中不可或缺的一部分。GPT-4&#xff0c;作为最新一代的语言模型&#xff0c;不仅继承了前代技术的优势&#xff0c;还在理解深度、生成连贯性和创造性方面实现了质的飞跃。本文将探讨GPT-4的创…

强化学习应用(二):基于Q-learning的无人机物流路径规划研究(提供Python代码)

一、Q-learning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个价值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…

【JAVA】谈谈 ReadWriteLock 和 StampedLock

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 ReadWriteLock&#xff08;读写锁&#xff09; 基本原理&#xff1a; 接口和实现&#xff1a; 用法示例&#xff1a; StampedL…

电梯导轨市场分析:我国市场销量增长率约为2.4%

电梯导轨(The elevator guide rail)是由钢轨和连接板构成的电梯构件&#xff0c;分为轿厢导轨和对重导轨。从截面形状分为T形&#xff0c;L形和空心三种形式。导轨在起导向作用的同时&#xff0c;承受轿厢&#xff0c;电梯制动时的冲击力&#xff0c;安全钳紧急制动时的冲击力等…

javacv和opencv对图文视频编辑-用多张图片合成MP4视频

狠人话不多&#xff0c;直接上代码 package com.bitar.javavideo.test;import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.Java2DFrameConverter;import j…

Camunda Cluster

Rest API&#xff1a;无状态&#xff0c;根据权重路由。控制台API&#xff1a;webapp 登录有状态&#xff0c;根据IP路由。 nginx.conf upstream rest_proxy {server localhost:8080 weight1;server localhost:8081 weight1;server localhost:8082 weight1; }upstream webapp…

《2024 年 Web3.0 数字资产趋势报告》(二)

撰文&#xff1a;方军、周芳鸽、李祺虹、张睿彬&#xff0c;Uweb 编辑&#xff1a;Nona&#xff0c;Techub News 点击关注公众号获取完整报告 接下来我们将继续和大家分享《2024 年 Web3.0 数字资产趋势报告》中其余部分。

PyCharm连接服务器(利用PyCharm实现远程开发)

利用PyCharm实现远程开发 注&#xff1a;该功能只有在PyCharm专业版下才可以使用&#xff0c;并且必须是官方的正版许可&#xff0c;破解版的是不可以使用的&#xff01;&#xff01;&#xff01;可以通过免费教育许可申请使用权限&#xff08;申请流程&#xff09;。 pycharm…

QEMU源码全解析 —— PCI设备模拟(6)

接前一篇文章&#xff1a; 上一回讲到了pci_edu_realize函数中的pci_register_bar函数&#xff0c;本回对于其进行详细解析。 再次贴出pci_register_bar函数源码&#xff0c;在hw/pci/pci.c中&#xff0c;代码如下&#xff1a; void pci_register_bar(PCIDevice *pci_dev, in…

在Java中正确使用Optional

Optional类是在Java 8中引入的&#xff0c;用于解决NullPointerException的问题。 java.util.Optional类是一个泛型类型的类&#xff0c;只包含一个类型为T的值。其目的是提供对可能为null的类型T的引用对象的更安全的替代方案。但是&#xff0c;只有在正确使用的情况下&#…

HarmonyOS开发FA应用模型下多个页面的声明方式

目录 方式1 方式2 HarmonyOS配套的IDE是DevEco Studio&#xff0c;目前的版本是3.1。官网可以直接下载 HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 ​ 方式1 ​在DevEco Studio如果是在pages目录通过右键New->ArkTS File生成的文件&#xff0c;需要注意&…

Android 13(T) - Media框架(3)- mediaserver

上一节我们了解到android_media_MediaPlayer.cpp中封装的是MediaPlayer native实现&#xff0c;这一节我们就来了解它的内部实现。 1、MediaPlayer MediaPlayer native代码位于frameworks/av/media/libmedia/mediaplayer.cpp 先来看MediaPlayer的声明&#xff0c;它继承于BnM…

鸿蒙原生应用再添新丁!天眼查 入局鸿蒙

鸿蒙原生应用再添新丁&#xff01;天眼查 入局鸿蒙 来自 HarmonyOS 微博1月12日消息&#xff0c;#天眼查启动鸿蒙原生应用开发#作为累计用户数超6亿的头部商业信息查询平台&#xff0c;天眼查可以为商家企业&#xff0c;职场人士以及普通消费者等用户便捷和安全地提供查询海量…

Vue高级

一 ref属性 被用来给元素或子组件注册引用信息&#xff08;id的替代者&#xff09; 应用在html标签上获取的是真实DOM元素&#xff0c;应用在组件标签上是组件实例对象&#xff08;vc&#xff09; 使用方式&#xff1a; 打标识&#xff1a; ...... 或 获取&#xff1a;this.…

ubuntu安装mysql(tar.xz)

1&#xff1a;下载地址 MySQL &#xff1a;&#xff1a; 下载 MySQL 社区服务器 2&#xff1a;上传文件到服务器 3:解压 mkdir mysqlmv mysql-8.0.13-linux-glibc2.12-x86_64.tar.xz /mysqlcd /mysqltar -xvf mysql-8.0.13-linux-glibc2.12-x86_64.tar.xzmv /mysql/mysql-8.…

【Spring类路径Bean定义信息扫描】

Spring类路径Bean定义信息扫描 1. ClassPathBeanDefinitionScanner作用2. 类声明3. 属性4. 构造器5. 扫描方法6. 真正扫描方法7. postProcessBeanDefinition8. 注册bean定义 1. ClassPathBeanDefinitionScanner作用 扫描类路径下的类注册为bean定义。2. 类声明 public class …

后端获取来访url

先说一下&#xff1a;后端是没有办法获取前端来访路径的&#xff1a;a.com访问到b.com&#xff0c;你的程序是b.com&#xff0c;你想获取a.com这个路径&#xff0c;在java后端是获取不到的&#xff0c;反正我网上搜了好久&#xff0c;用了好多种方法都没有获取到&#xff0c;如…

【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】

前言 大家好吖&#xff0c;欢迎来到 YY 滴C考前速过系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《…

行为型设计模式——状态模式

状态模式 状态模式是比较简单的设计模式&#xff0c;它的主要作用是减少代码中大量的 if-else 或者 switch-case 等逻辑判断&#xff08;俗称屎山&#xff09;。它将每个状态定义为一个类&#xff0c;而每个状态类有自己对应的方法&#xff0c;因此当需要根据状态执行逻辑代码…