Springboot AOP接口防刷、防重复提交

Java利用注解、Redis做防重复提交和限流
使用场景
用户网络慢,电脑卡,一直点击保存,修改按钮无返回信息,会导致多个请求去保存、修改
开放接口、或加密接口频繁访问,会导致程序压力大,可能被他人写脚本一直请求接口
解决方案
前端js提交后禁止按钮,返回结果后解禁(前端不严谨,点击速度快,也可重复提交)
在java中添加自定义防重复提交注解 @RepeatSubmit ,利用AOP切入,其次用Redis临时存入唯一信息。开放接口把请求的IP、请求路径、请求的电脑User-Agent拼接为唯一key,未开发接口按照使用场景,组装为唯一key
等等…             

首当其冲肯定是先引入AOP依赖,maven为例 pom.xml

<!-- aop依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- JSON依赖 -->
<dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version><classifier>jdk15</classifier>
</dependency>
<!-- redis依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

有了AOP的支持 接下来我们进行自定义注解 NoRepeatSubmit

package cn.tpson.parking.module.base.params.annotate;import java.lang.annotation.*;/*** 自定义防重提交注解*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {/*** 默认防重提交是方法参数*/Type limitType() default Type.PARAM;/*** 加锁过期时间,默认是 5s* 比如通过redis的key来校验是否重复提交,* 这个5s就是设置的key的过期时间*/long lockTime() default 5;/*** 防重提交,支持两种,一个方法参数,一个是令牌*/enum  Type {PARAM,TOKEN }}

定义AOP切面类:RepeatSubmitAspect,现在定义两种重复提交或限流,

第一种:获取用户电脑信息、获取请求IP地址、获取请求Url 。

第二种:获取请求里的token、获取请求IP地址。如不符合场景,可在repeatSubmit环绕通知方法中重写。注(方法中使用获取IP工具类、常量类,CommonConstant为常量,可直接去创建)

package cn.tpson.parking.module.base.aspect;import cn.tpson.parking.framework.common.util.IpKit;
import cn.tpson.parking.module.base.params.annotate.RepeatSubmit;
import cn.xtool.core.rest.Result;
import lombok.RequiredArgsConstructor;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;@Aspect
@RequiredArgsConstructor
@Component
public class RepeatSubmitAspect {protected static final Logger logger = LoggerFactory.getLogger(RepeatSubmitAspect.class);private final RedisTemplate<String, Object> redisTemplate;@Pointcut("@annotation(repeatSubmit)")public void pointNoRepeatSubmit(RepeatSubmit repeatSubmit) {}/*** 利用Redis实现的防重复提交拦截器。** @param joinPoint    切面连接点,表示被拦截的方法。* @param repeatSubmit 重复提交注解对象,包含锁的时间等配置。* @return 返回方法执行结果,若重复提交则返回失败信息。* @throws Throwable 如果方法执行过程中出现异常,则抛出。*/@Around(value = "pointNoRepeatSubmit(repeatSubmit)", argNames = "joinPoint,repeatSubmit")public Object repeatSubmit(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {logger.info("-----------防止重复提交开始----------");// 获取当前请求的属性ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes == null) {logger.error("ServletRequestAttributes is null.");return Result.fail("系统异常");}// 从请求中获取必要的信息来构建唯一键HttpServletRequest request = attributes.getRequest();String key = buildKey(request, repeatSubmit);// 尝试加锁,防止重复提交if (!tryLock(key, repeatSubmit.lockTime())) {String repeatMsg = "请勿重复提交或者操作过于频繁! 请在" + repeatSubmit.lockTime() + "秒后重试";logger.info(repeatMsg);return Result.fail(repeatMsg);}try {logger.debug("通过,执行下一步");// 执行被拦截的方法Object o = joinPoint.proceed();logger.info("----------防止重复提交设置结束----------");return o;} catch (Exception e) {logger.error("方法执行异常", e);throw e;} finally {// 无论方法执行结果如何,最后都释放锁redisTemplate.delete(key);}}/*** 尝试对给定的键加锁。** @param key      键名,用于Redis中标识一个锁。* @param lockTime 锁定的时间,单位秒。* @return 如果加锁成功返回true,否则返回false。*/private boolean tryLock(String key, Long lockTime) {Boolean locked = redisTemplate.opsForValue().setIfAbsent(key, key);if (Boolean.TRUE.equals(locked)) {redisTemplate.expire(key, lockTime, TimeUnit.SECONDS);return true;}return false;}/*** 根据请求信息和注解配置构建唯一键。** @param request      HttpServletRequest对象,用于获取请求信息。* @param repeatSubmit 重复提交注解对象,配置限制类型等。* @return 返回构建好的唯一键字符串。*/private String buildKey(HttpServletRequest request, RepeatSubmit repeatSubmit) {StringBuilder key = new StringBuilder();String limitType = repeatSubmit.limitType().name();// 根据限制类型构建键名if (limitType.equalsIgnoreCase(RepeatSubmit.Type.PARAM.name())) {key.append(IpKit.getIpAdrress(request)).append("-").append(request.getRequestURI());}logger.info("防止重复提交Key:{}", key);return key.toString();}
}

其中ResultAPI为统一返回结果

使用示例: 

    @PutMapping("/sendLoginCode")@ApiOperation(value = "发送登录验证码")@RepeatSubmit(lockTime = 60L, limitType = RepeatSubmit.Type.PARAM)public Result<Boolean> sendLoginCode(@ApiIgnore HttpServletRequest request, @Valid @RequestBody PhoneLoginSendCodeDTO dto) {validCaptcha(request, dto.getCode());return Result.ok(userService.sendLoginCode(dto, PlatformTypeEnum.CUSTOMER));}

第一次访问结果

 第二次访问

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

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

相关文章

Godot3D学习笔记1——界面布局简介

创建完成项目之后可以看到如下界面&#xff1a; Godot引擎也是场景式编程&#xff0c;这里的一个场景相当于一个关卡。 这里我们点击左侧“3D场景”按钮创建一个3D场景&#xff0c;现在在中间的画面中会出现一个球。在左侧节点视图中选中“Node3D”&#xff0c;右键创建子节点…

企业车辆管理系统平台是做什么的?

企业车辆管理系统平台是一种综合性的管理系统&#xff0c;它主要集车辆信息管理、车辆调度、车辆维修、油耗管理、驾驶员管理以及报表分析等多种功能于一体。通过这个平台&#xff0c;企业可以实现对车辆的全面管理&#xff0c;优化车辆使用效率&#xff0c;降低运营成本&#…

在Windows 10中禁用Windows错误报告的4种方法,总有一种适合你

序言 在本文中&#xff0c;我们的主题是如何在Windows 10中禁用Windows错误报告。你知道什么是Windows错误报告吗&#xff1f;事实上&#xff0c;Windows错误报告有助于从用户的计算机收集有关硬件和软件问题的信息&#xff0c;并将这些信息报告给Microsoft。 它将检查任何可…

基于postCSS手写postcss-px-to-vewiport插件实现移动端适配

&#x1f31f;前言 目前前端实现移动端适配方案千千万&#xff0c;眼花缭乱各有有缺&#xff0c;但目前来说postcss-px-to-vewiport是一种非常合适的实现方案&#xff0c;postcss-px-to-vewiport是一个基于postCss开发的插件&#xff0c;其原理就是将项目中的px单位转换为vw(视…

day07 51单片机-18B20温度检测

18B20温度检测 1.1 需求描述 本案例讲解如何从18B20传感器获取温度信息并显示在LCD上。 1.2 硬件设计 1.2.1 硬件原理图 1.2.3 18B20工作原理 可以看到18B20有两根引脚负责供电,一根引脚负责数据交换。18B20就是通过数据线和单片机进行数据交换的。 1)18B20工作时序 2)…

node.js-模块化

定义&#xff1a;CommonJS模块是为Node.js打包Javascript代码的原始方式。Node.js还支持浏览器和其他Javascript运行时使用的ECMAScript模块标准。 在Node.js中&#xff0c;每个文件都被视为一个单独的模块。 概念&#xff1a;项目是由很多个模块文件组成的 好处&#xff1a…

找不到msvcp140dll,无法继续执行代码的详细解决方法

在我们日常使用计算机进行各类工作任务的过程中&#xff0c;时常会遭遇一些突发的技术问题。比如&#xff0c;有时在运行某个重要程序或应用软件时&#xff0c;系统会突然弹出一个令人困扰的错误提示&#xff1a;“电脑提示找不到msvcp140.dll文件&#xff0c;因此无法继续执行…

AI预测福彩3D第9套算法实战化测试第1弹2024年4月22日第1次测试

经过前面多套算法的测试&#xff0c;总结了一些规律&#xff0c;对模型优化了一些参数&#xff0c;比如第8套算法的测试&#xff0c;7码的命中率由最开始的20%提高到了50%。虽然命中率有了很大的提高&#xff0c;但是由于咱们之前的算法只是为了测试和记录&#xff0c;提供的方…

20.Unity飞机大战游戏

1任务&#xff1a;使背景图动起来 2任务&#xff1a;飞机换帧动画 3任务&#xff1a;让飞机发射子弹 4任务&#xff1a;敌机出现 5任务&#xff1a;控制飞机 6任务&#xff1a;游戏碰撞逻辑 7任务&#xff1a;另外两种类型的敌机 8任务&#xff1a;拾取奖励物品换枪 9…

C语言中与内存操作有关的一些函数

前提 最近在使用C语言在开发项目时&#xff0c;要对内存进行操作。刚开始写的时候有一点迷糊&#xff0c;看了一些东西后才发现为什么说指针是C语言的灵魂&#xff0c;因为它可以对内存直接进行操作&#xff0c;多么帅的事情&#xff0c;真的是太帅了。 malloc 声明在头文件…

YOLOv9改进策略 | Conv篇 | 利用 Haar 小波的下采样HWD替换传统下采样(改变YOLO传统的Conv下采样)

一、本文介绍 本文给大家带来的改进机制是Haar 小波的下采样HWD替换传统下采样&#xff08;改变YOLO传统的Conv下采样&#xff09;在小波变换中&#xff0c;Haar小波作为一种基本的小波函数&#xff0c;用于将图像数据分解为多个层次的近似和细节信息&#xff0c;这是一种多分…

Pytorch 的数据处理 学习笔记

一. 数据集Dataset Dataset是一个抽象类&#xff0c;用于表示数据集。可以创建自定义的Dataset类来加载和准备好的数据&#xff0c;无论数据是存储在何处&#xff08;例如磁盘、内存或其他位置&#xff09;。PyTorch提供了一些内置的Dataset类&#xff0c;例如TensorDataset、…

【Interconnection Networks 互连网络】Flattened Butterfly 扁平蝶形拓扑

Flattened Butterfly 扁平蝶形拓扑 1. 传统蝶形网络 Butterfly Topology2. 扁平蝶形拓扑 Flattened Butterfly3.On-Chip Flattened Butterfly 扁平蝶形拓扑应用于片上网络 Flattened Butterfly 扁平蝶形拓扑 扁平蝶形拓扑是一种经济高效的拓扑&#xff0c;适用于高基数路由器…

【机器学习】《机器学习建模基础》笔记

文章目录 单元0 前言单元1 数学建模与机器学习学习目标&#xff08;一&#xff09;什么是模型&#xff08;二&#xff09;数学模型的分类&#xff08;三&#xff09;数学建模的一般步骤&#xff08;四&#xff09;机器学习的概念&#xff08;五&#xff09;机器学习的分类&…

【C++】友元--最全解析(友元是什么?我们应该如何理解友元?友元可以应用在那些场景?)

目录 一、前言 二、友元是什么&#xff1f; 三、友元的感性理解和分类 &#x1f95d;友元的感性理解 &#x1f34b;友元的三种分类 ✨友元 --- 全局函数 ✨友元 --- 成员函数 ✨友元 --- 类 四、友元函数的应用场景 &#x1f34d;操作符重载 :"<<" 与…

Oracle 可传输表空间(Transportable Tablespace)

在数据归档、备份、测试等场景&#xff0c;我们经常需要将数据从一个系统移动到另一个系统&#xff0c;一个较常用的方案是数据的导出/导入&#xff08;export/import&#xff09;&#xff0c;但是在数据量较大的场景&#xff0c;此方案可能比较耗时。而可传输表空间是一种以文…

在Linux系统中搜索当前路径及其子目录下所有PDF文件中是否包含特定字符串

目录标题 方法一&#xff1a;pdfgrep方法二&#xff1a;使用find和xargs与pdftotext&#xff08;将PDF转换为文本&#xff09;组合&#xff0c;然后用grep搜索 方法一&#xff1a;pdfgrep pdfgrep -ri "rockchip" .方法二&#xff1a;使用find和xargs与pdftotext&am…

消息队列选型(RabbitMq、RocketMq、Kafaka)

文章目录 前言RabbitMq优点缺点 RocketMq优点缺点 Kafaka优点缺点 总结 前言 当引入消息队列时&#xff0c;常见的选择包括ActiveMQ、Kafka、RabbitMQ和RocketMQ。然而&#xff0c;近年来&#xff0c;ActiveMQ的活跃度已经下降&#xff0c;很多公司已经不再使用这款消息队列中…

wangEditor集成Word导入(富文本编辑器)

wangEditor集成Word导入(富文本编辑器)&#xff0c;wangEditor – 支持word上传的富文本编辑器&#xff0c;WANGEDITOR实现WORD图片自动转存&#xff0c;JAVA中将WORD转换为HTML导入到WANGEDITOR编辑器中&#xff08;解决图片问题&#xff0c;样式&#xff0c;非常完美&#xf…

视频怎么做成二维码分享?微信扫码播放视频的操作技巧

随着互联网的快速发展&#xff0c;现在大家喜欢通过扫描二维码的方式来获取信息&#xff0c;比如现在通过扫码看视频就是很常见的一种方式。生成二维码的方式可以实现内容传递的简单化&#xff0c;而且可以有效的降低制作者的成本&#xff0c;提升用户的体验效果。 现在使用比…