接口防刷!利用redisson快速实现自定义限流注解

news2024/9/22 13:36:53

问题:
在日常开发中,一些重要的对外接口,需要加上访问频率限制,以免造成资��损失。

如登录接口,当用户使用手机号+验证码登录时,一般我们会生成6位数的随机验证码,并将验证码有效期设置为1-3分钟,如果对登录接口不加以限制,理论上,通过技术手段,快速重试100000次,即可将验证码穷举出来。

解决思路:对登录接口加上限流操作,如限制一分钟内最多登录5次,登录次数过多,就返回失败提示,或者将账号锁定一段时间。

实现手段:利用redis的有序集合即Sorted Set数据结构,构造一个令牌桶来实施限流。而redisson已经帮我们封装成了RRateLimiter,通过redisson,即可快速实现我们的目标。

  • 定义一个限流注解

      import org.redisson.api.RateIntervalUnit;
    
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
    
      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface GlobalRateLimiter {
    
      	String key();
    
      	long rate();
    
      	long rateInterval() default 1L;
    
      	RateIntervalUnit rateIntervalUnit() default RateIntervalUnit.SECONDS;
    
      }
    
  • 利用aop进行切面

      import com.zj.demoshow.annotion.GlobalRateLimiter;
      import lombok.extern.slf4j.Slf4j;
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Pointcut;
      import org.aspectj.lang.reflect.MethodSignature;
      import org.redisson.Redisson;
      import org.redisson.api.RRateLimiter;
      import org.redisson.api.RateIntervalUnit;
      import org.redisson.api.RateType;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.core.DefaultParameterNameDiscoverer;
      import org.springframework.expression.Expression;
      import org.springframework.expression.ExpressionParser;
      import org.springframework.expression.spel.standard.SpelExpressionParser;
      import org.springframework.expression.spel.support.StandardEvaluationContext;
      import org.springframework.stereotype.Component;
    
      import javax.annotation.Resource;
      import java.lang.reflect.Method;
      import java.util.concurrent.TimeUnit;
    
      @Aspect
      @Component
      @Slf4j
      public class GlobalRateLimiterAspect {
    
      	@Resource
      	private Redisson redisson;
      	@Value("${spring.application.name}")
      	private String applicationName;
      	private final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
    
      	@Pointcut(value = "@annotation(com.zj.demoshow.annotion.GlobalRateLimiter)")
      	public void cut() {
      	}
    
      	@Around(value = "cut()")
      	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
      		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
      		Method method = methodSignature.getMethod();
      		String className = method.getDeclaringClass().getName();
      		String methodName = method.getName();
      		GlobalRateLimiter globalRateLimiter = method.getDeclaredAnnotation(GlobalRateLimiter.class);
      		Object[] params = joinPoint.getArgs();
      		long rate = globalRateLimiter.rate();
      		String key = globalRateLimiter.key();
      		long rateInterval = globalRateLimiter.rateInterval();
      		RateIntervalUnit rateIntervalUnit = globalRateLimiter.rateIntervalUnit();
      		if (key.contains("#")) {
      			ExpressionParser parser = new SpelExpressionParser();
      			StandardEvaluationContext ctx = new StandardEvaluationContext();
      			String[] parameterNames = discoverer.getParameterNames(method);
      			if (parameterNames != null) {
      				for (int i = 0; i < parameterNames.length; i++) {
      					ctx.setVariable(parameterNames[i], params[i]);
      				}
      			}
      			Expression expression = parser.parseExpression(key);
      			Object value = expression.getValue(ctx);
      			if (value == null) {
      				throw new RuntimeException("key无效");
      			}
      			key = value.toString();
      		}
      		key = applicationName + "_" + className + "_" + methodName + "_" + key;
      		log.info("设置限流锁key={}", key);
      		RRateLimiter rateLimiter = this.redisson.getRateLimiter(key);
      		if (!rateLimiter.isExists()) {
      			log.info("设置流量,rate={},rateInterval={},rateIntervalUnit={}", rate, rateInterval, rateIntervalUnit);
      			rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, rateIntervalUnit);
      			//设置一个过期时间,避免key一直存在浪费内存,这里设置为延长5分钟
      			long millis = rateIntervalUnit.toMillis(rateInterval);
      			this.redisson.getBucket(key).expire(Long.sum(5 * 1000 * 60, millis), TimeUnit.MILLISECONDS);
      		}
      		boolean acquire = rateLimiter.tryAcquire(1);
      		if (!acquire) {
      			//这里直接抛出了异常  也可以抛出自定义异常,通过全局异常处理器拦截进行一些其他逻辑的处理
      			throw new RuntimeException("请求频率过高,此操作已被限制");
      		}
      		return joinPoint.proceed();
      	}
      }
    

ok,通过以上两步,即可完成我们的限流注解了,下面通过一个接口验证下效果。

新建一个controller,写一个模拟登录的方法。

@RestController
@RequestMapping(value = "/user")
public class UserController {


	@PostMapping(value = "/testForLogin")
	//以account为锁的key,限制每分钟最多登录5次
	@GlobalRateLimiter(key = "#params.account", rate = 5, rateInterval = 60)
	R<Object> testForLogin(@RequestBody @Validated LoginParams params) {
		//登录逻辑
		return R.success("登录成功");
	}
}

启动服务,通过postman访问此接口进行验证。
image

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

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

相关文章

【论文解读】VoxelNeXt: Fully Sparse VoxelNet for 3D Object Detection and Tracking

VoxelNeXt 摘要引言方法Sparse CNN Backbone AdaptationSparse Prediction Head 3D Tracking实验结论 摘要 3D物体检测器通常依赖于手工制作的方法&#xff0c;例如锚点或中心&#xff0c;并将经过充分学习的2D框架转换为3D。因此&#xff0c;稀疏体素特征需要通过密集预测头进…

电脑没有声音了怎么恢复?3个硬核操作,解救静音危机!

当你沉迷于电脑中的音乐、电影或是游戏时&#xff0c;突然一阵寂静袭来&#xff0c;是不是感觉就像突然按下了暂停键&#xff1f;这无疑是一场大灾难&#xff01;电脑没有声音了怎么恢复呢&#xff1f;急&#xff0c;今天小编带来了3个硬核操作&#xff0c;让你从无声的幽谷中爬…

二、BIO、NIO、直接内存与零拷贝

一、网络通信编程基础 1、Socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层&#xff0c;是一组接口&#xff0c;由操作系统提供&#xff1b; Socket将复杂的TCP/IP协议处理和通信缓存管理都隐藏在接口后面&#xff0c;对用户来说就是使用简单的接口进行网络应用编程…

【python】OpenCV—Scanner

文章目录 1、需求描述2、代码实现3、涉及到的库函数cv2.arcLengthcv2.approxPolyDPskimage.filters.threshold_localimutils.grab_contours 4、完整代码5、参考 1、需求描述 输入图片 扫描得到如下的结果 用OpenCV构建文档扫描仪只需三个简单步骤: 1.边缘检测 2.使用图像中…

02线性表 - 链表

这里是只讲干货不讲废话的炽念&#xff0c;这个系列的文章是为了我自己以后复习数据结构而写&#xff0c;所以可能会用一种我自己能够听懂的方式来描述&#xff0c;不会像书本上那么枯燥和无聊&#xff0c;且全系列的代码均是可运行的代码&#xff0c;关键地方会给出注释^_^ 全…

windows edge自带的pdf分割工具(功能)

WPS分割pdf得会员&#xff0c;要充值&#xff01;网上一顿乱找&#xff0c;发现最简单&#xff0c;最好用&#xff0c;免费的还是回到Windows。 Windows上直接在edge浏览器打开PDF&#xff0c;点击 打印 按钮,页面下选择对应页数 打印机 选择 另存为PDF&#xff0c;然后保存就…

memcached 高性能内存对象缓存

memcached 高性能内存对象缓存 memcache是一款开源的高性能分布式内存对象缓存系统&#xff0c;常用于做大型动态web服务器的中间件缓存。 mamcached做web服务的中间缓存示意图 当web服务器接收到请求需要处理动态页面元素时&#xff0c;通常要去数据库调用数据&#xff0c;但…

ProtoBuf的安装(win+ubuntu+centos版本)

Win下安装ProtoBuf教程 WProtoBuf Win版本 上方链接就是ProtoBuf官方在Github上面的仓库&#xff0c;我这里下的是21.11版本&#xff0c;至于你要下哪个版本&#xff0c;可以根据自己的需要去下载。 首先点击链接&#xff0c;进入首页&#xff0c;向下滑就可以找到ProtoBuf…

加密传输及相关安全验证:

1.1. 加密&#xff1a; 1.1.1. 对称加密&#xff1a; 特点&#xff1a;加解密用一个密钥&#xff0c;加解密效率高&#xff0c;速度快&#xff0c;有密钥交互的问题问题&#xff1a;双方如何交互对称密钥的问题&#xff0c;用非对称密钥的公钥加密对称密钥的混合加密方式常用…

IP溯源工具--IPTraceabilityTool

工具地址&#xff1a;xingyunsec/IPTraceabilityTool: 蓝队值守利器-IP溯源工具 (github.com) 工具介绍&#xff1a; 在攻防演练期间&#xff0c;对于值守人员&#xff0c;某些客户要求对攻击IP都进行分析溯源&#xff0c;发现攻击IP的时候&#xff0c;需要针对攻击IP进行分析…

PHP手边酒店多商户版平台小程序系统源码

&#x1f3e8;【旅行新宠】手边酒店多商户版小程序&#xff0c;一键解锁住宿新体验&#xff01;&#x1f6cc; &#x1f308;【开篇&#xff1a;旅行新伴侣&#xff0c;尽在掌握】&#x1f308; 还在为旅行中的住宿选择而纠结吗&#xff1f;是时候告别繁琐的搜索和比价过程&a…

js继承之构造函数继承

最近在看js红宝书&#xff0c;学到了继承这一章节&#xff0c;看到了下图这段代码根据自己理解不明白为什么两次实例的colors值不一样 又是自己画图又是查找资料看别人如何理解的&#xff0c;今天才按自己的理解搞明白为啥。可能我的理解也是有偏差错误的&#xff0c;希望佬可以…

开源防病毒工具--ClamAV

产品文档&#xff1a;简介 - ClamAV 文档 开源地址&#xff1a;Cisco-Talos/clamav&#xff1a;ClamAV - 文档在这里&#xff1a;https://docs.clamav.net (github.com) 一、引言 ClamAV&#xff08;Clam AntiVirus&#xff09;是一个开源的防病毒工具&#xff0c;广泛应用…

【Node.js】会话控制

express 中操作 cookie cookie 是保存在浏览器端的一小块数据。 cookie 是按照域名划分保存的。 浏览器向服务器发送请求时&#xff0c;会自动将 当前域名下可用的 cookie 设置在请求头中&#xff0c;然后传递给服务器。 这个请求头的名字也叫 cookie &#xff0c;所以将 c…

NVidia 的 gpu 开源 Linux Kernel Module Driver 编译 安装 使用

见面礼&#xff0c;动态查看gpu使用情况&#xff0c;每隔2秒钟自动执行一次 nvidia-smi $ watch -n 2 nvidia-smi 1&#xff0c;找一台nv kmd列表中支持的 GPU 的电脑&#xff0c;安装ubuntu22.04 列表见 github of the kmd source code。 因为 cuda sdk 12.3支持最高到 ubu…

AMEYA360:思瑞浦推出汽车级理想二极管ORing控制器TPS65R01Q

聚焦高性能模拟芯片和嵌入式处理器的半导体供应商思瑞浦3PEAK(股票代码&#xff1a;688536)发布汽车级理想二极管ORing控制器TPS65R01Q。 TPS65R01Q拥有20mV正向调节功能&#xff0c;降低系统损耗。快速反向关断(Typ&#xff1a;0.39μs)&#xff0c;在电池反向和各种汽车电气瞬…

OCR拍照识别采购单视频介绍

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

【BUG】已解决:NOAUTH Authentication required

已解决&#xff1a;NOAUTH Authentication required 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社区主理人…

Atlas架构与原理

作者&#xff1a;楼高 一、总体架构 Atlas 是一个可伸缩且功能丰富的数据管理系统&#xff0c;深度集成了 Hadoop 大数据组件。简单理解就是一个 跟 Hadoop 关系紧密的&#xff0c;可以用来做元数据管理的一个系统,整个结构图如下所示&#xff1a; Atlas可以分为以下几层&…

IAR嵌入式开发解决方案已全面支持芯科集成CX3288系列车规RISC-V MCU,共同推动汽车高品质应用的安全开发

中国上海&#xff0c;2024年7月16日 — 全球领先的嵌入式系统开发软件解决方案供应商IAR与芯科集成电路&#xff08;以下简称“芯科集成”&#xff09;联合宣布&#xff0c;最新版本IAR Embedded Workbench for RISC-V 3.30.2功能安全版已全面支持芯科集成CX3288系列车规RISC-V…