SpringBoot+AOP+Redis 防止重复请求提交

本文项目基于以下教程的代码版本: https://javaxbfs.blog.csdn.net/article/details/135224261

代码仓库: springboot一些案例的整合_1: springboot一些案例的整合

1、实现步骤

2.引入依赖

我们需要redis、aop的依赖。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.6</version>
</dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version>
</dependency>

redis的访问参数,默认的就行,不需要特别配置。

3、定义拦截注解

我们最终希望的效果是,你想要哪个方法有防止重复提交的功能,直接加上@RepeatSubmit注解即可。 代码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {/*** 加锁过期时间,默认是5秒* @return*/long lockTime() default 5L;
}

这段代码定义了一个Java注解(Annotation)叫做RepeatSubmit。注解是Java提供的一种元数据机制,它可以被用于为代码提供附加的信息,这些信息可以被编译器用于生成代码、生成文档、代码检查等。

下面是对这段代码的详细解释:

  • @Target(ElementType.METHOD): 这个注解指定RepeatSubmit只能被用于方法上。ElementType.METHOD表示这个注解只能用于方法。

  • @Retention(RetentionPolicy.RUNTIME): 这个注解指定了RepeatSubmit的保留策略是RUNTIME。这意味着在运行时,这个注解仍然可以被读取。

  • @Documented: 这个注解表明RepeatSubmit应当被Java文档生成器包含在生成文档中。

  • public @interface RepeatSubmit: 定义了一个名为RepeatSubmit的公共注解。

  • long lockTime() default 5L: 这是RepeatSubmit注解的一个元素,名为lockTime。这个元素返回一个长整型值,并且有一个默认值5秒(5L表示5秒)。

这个注解是为了防止方法的重复提交,并提供了一个默认的加锁过期时间为5秒。如果一个方法被这个注解标记,那么在特定的加锁过期时间内,这个方法只会被执行一次。

4、实现防止重复提交切面

关于Spring Aop切面,可以看我之前的文章。

NoRepeatSubmitAspect

package com.it.demo.aspect;import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.it.demo.annotation.RepeatSubmit;
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.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.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.TimeUnit;@Component
@Slf4j
@Aspect
public class NoRepeatSubmitAspect {@Resourceprivate RedisTemplate redisTemplate;@Pointcut("@annotation(repeatSubmit)")public void pointCutNoRepeatSubmitAspect(RepeatSubmit repeatSubmit) {}@Around("pointCutNoRepeatSubmitAspect(repeatSubmit)")public Object around(ProceedingJoinPoint joinPoint,RepeatSubmit repeatSubmit) throws Throwable {String reqSignature = buildReqSignature(joinPoint);//加锁时间long lockTime = repeatSubmit.lockTime();Boolean res = redisTemplate.opsForValue().setIfAbsent(reqSignature, 1, lockTime, TimeUnit.SECONDS);if(!res){throw new RuntimeException("重复请求!");}return joinPoint.proceed();}/*** 构建请求签名* @param joinPoint* @return*/private String buildReqSignature(ProceedingJoinPoint joinPoint) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(Objects.isNull(requestAttributes)){throw new RuntimeException("请求参数异常!");}HttpServletRequest request = requestAttributes.getRequest();String remoteAddr =request.getRemoteAddr()  + ":" +  request.getRemoteHost();String method = request.getMethod();String requestURI = request.getRequestURI();StringBuffer sb = new StringBuffer();for (Object arg : joinPoint.getArgs()) {sb.append(arg);}//请求签名(MD5加密)return DigestUtil.md5Hex(StrUtil.format("{}-{}-{}-{}",remoteAddr,method,requestURI,sb));}
}

这段代码是一个切面(Aspect),用于处理重复提交的情况。以下是对代码中的主要部分的解释:

  1. 导入包及注解:代码首先导入了一些类,并使用了一些注解,例如 @Component, @Aspect, @Resource, @Slf4j 等。

  2. NoRepeatSubmitAspect 类:这是一个切面类,用于实现对重复提交的处理逻辑。

  3. 成员变量:代码中使用了 @Resource 注解注入了一个 RedisTemplate 对象,用于操作 Redis。

  4. 切入点:使用 @Pointcut 注解定义了一个切入点,这里使用了 @annotation(repeatSubmit) 表达式,表示会拦截所有标注了 @RepeatSubmit 注解的方法。

  5. Around 通知:使用 @Around 注解定义了一个环绕通知,在拦截的方法执行前后会执行这里定义的逻辑。

  6. around() 方法:在该方法中,首先调用了 buildReqSignature(joinPoint) 方法构建了请求的签名信息,然后根据 RepeatSubmit 注解中的配置,在 Redis 中设置了一个锁,以防止重复提交。如果设置锁失败,则抛出了一个运行时异常,表示重复请求,否则执行被拦截的方法。

  7. buildReqSignature() 方法:该方法用于构建请求的签名信息,首先获取了请求相关的信息,然后将这些信息进行拼接,并使用 MD5 加密生成请求签名。

这段代码是一个使用 AOP 实现的防止重复提交的切面,在方法执行前通过生成请求签名并利用 Redis 进行锁定的方式,来避免重复提交。

环绕通知实现逻辑如下图所示

构建 Redis 加锁需要使用的 key,加锁key由指定前缀 + MD5(请求签名)组成,对请求签名进行MD5,一方面减少Key的长度,另一方面保证签名信息中的敏感信息不可见,其实现逻辑如下图所示:

/*** 构建请求签名* @param joinPoint* @return*/
private String buildReqSignature(ProceedingJoinPoint joinPoint) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(Objects.isNull(requestAttributes)){throw new RuntimeException("请求参数异常!");}HttpServletRequest request = requestAttributes.getRequest();String remoteAddr =request.getRemoteAddr()  + ":" +  request.getRemoteHost();String method = request.getMethod();String requestURI = request.getRequestURI();StringBuffer sb = new StringBuffer();for (Object arg : joinPoint.getArgs()) {sb.append(arg);}//请求签名(MD5加密)return DigestUtil.md5Hex(StrUtil.format("{}-{}-{}-{}",remoteAddr,method,requestURI,sb));
}

5、 测试

查询方法加一个@RepeatSubmit

@GetMapping("/list")
@RepeatSubmit
public List<Device> list(){List<Device> list = deviceService.list();System.out.println(list);return deviceService.list();
}

访问localhost:8080/v1/device/list

五秒内再访问,报错:

{"code": 400,"msg": "重复请求!","data": null
}

验证通过。

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

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

相关文章

Typora使用PicGo+Gitee上传图片报错403 Forbidden

Typora使用PicGoGitee上传图片报错403 Forbidden Typora使用PicGoGitee上传图片&#xff0c;上传失败了&#xff0c;错误信息如下 打开PicGo的日志文件查看&#xff0c;可以看到错误详情如下 换了一个插件github-plus重新配置&#xff0c;解决了这个问题 再打开日志查看&…

扫雷(c语言)

先开一个test.c文件用来游戏的逻辑测试&#xff0c;在分别开一个game.c文件和game.h头文件用来实现游戏的逻辑 主要步骤&#xff1a; 游戏规则&#xff1a; 输入1&#xff08;0&#xff09;开始&#xff08;结束&#xff09;游戏&#xff0c;输入一个坐标&#xff0c;如果该坐…

【网络安全 | CTF】FlatScience

该题考察SQL注入 正文 后台扫到robots.txt 页面内容如下&#xff1a; 进入login.php 页面源代码如图&#xff1a; 传参debug得到php代码&#xff1a; <?php if(isset($_POST[usr]) && isset($_POST[pw])){$user $_POST[usr];$pass $_POST[pw];$db new SQLite3…

CEC2017(Python):五种算法(HHO、RFO、OOA、PSO、GWO)求解CEC2017

一、5种算法简介 1、哈里斯鹰优化算法HHO 2、红狐优化算法RFO 3、鱼鹰优化算法OOA 4、粒子群优化算法PSO 5、灰狼优化算法GWO 二、CEC2017简介 参考文献&#xff1a; [1]Awad, N. H., Ali, M. Z., Liang, J. J., Qu, B. Y., & Suganthan, P. N. (2016). “Problem d…

MySQL运维实战(1.2)安装部署:使用二进制安装部署

作者&#xff1a;俊达 引言 上一篇我们使用了RPM进行安装部署&#xff0c;这是一种安装快速、简化部署和管理过程、与操作系统提供的包管理工具紧密集成的部署方法。此外&#xff0c;当你需要更高的灵活性和自定义性&#xff0c;并且愿意承担一些额外的手动配置和管理工作&am…

计算机网络——应用层与网络安全(六)

前言&#xff1a; 前几章我们已经对TCP/IP协议的下四层已经有了一个简单的认识与了解&#xff0c;下面让我们对它的最顶层&#xff0c;应用层进行一个简单的学习与认识&#xff0c;由于计算机网络多样的连接形式、不均匀的终端分布&#xff0c;以及网络的开放性和互联性等特征&…

MR实战:统计总分与平均分

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、创建Maven项目2、添加相关依赖3、创建日志属性文件4、创建成绩映射器类5、创建成绩驱动器类6、启…

ubuntu虚拟机终端(terminal)打不开

问题描述 ubuntu&#xff08;16.04 LTS 64位&#xff09;虚拟机终端&#xff08;terminal&#xff09;打不开 问题原因 我是ctrlaltF5&#xff09;进入命令行模式&#xff0c;也就是无图形界面&#xff0c;这时候会让你输入用户名和密码: 运行命令: gnome-terminal 会看到有…

年底离职潮来了!来聊聊程序员的离职跳槽

每当元旦春节将至的时候&#xff0c;办公室的气氛也诡异起来&#xff0c;空气弥漫着离职的味道。因为积累许久的负面情绪长期无法获得释放&#xff0c;打工人对工作的容忍度越发稀薄了起来&#xff0c;有的打工人看似正襟危坐地坐在工位上&#xff0c;实则愤然辞职的念头在心里…

AI超级个体:ChatGPT与AIGC实战指南

目录 前言 一、ChatGPT在日常工作中的应用场景 1. 客户服务与支持 2. 内部沟通与协作 3. 创新与问题解决 二、巧用ChatGPT提升工作效率 1. 自动化工作流程 2. 信息整合与共享 3. 提高决策效率 三、巧用ChatGPT创造价值 1. 优化产品和服务 2. 提高员工满意度和留任率…

面试题:MySQL 自增主键一定是连续的吗?

文章目录 测试环境&#xff1a;一、自增值的属性特征&#xff1a;1. 自增主键值是存储在哪的&#xff1f;2. 自增主键值的修改机制&#xff1f; 二、新增语句自增主键是如何变化的&#xff1a;三、自增主键值不连续情况&#xff1a;&#xff08;唯一主键冲突&#xff09;四、自…

Markdown 常用语法

介绍 Markdown 是一种轻量级标记语言&#xff0c;创始人为约翰格鲁伯&#xff08;John Gruber&#xff09;。 它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。这种语言吸收了很多在电子邮件中已有的纯文…

nodeJS搭建免费代理IP池爬取贴吧图片实战

之前用python写过爬虫&#xff0c;这次想试试nodeJS爬虫爬取贴吧图片&#xff0c;话不多说代码如下&#xff0c;爬取制定吧的前十页所有帖子里的图片 爬取贴吧图片脚本 你得提前创建一个images文件夹 const axios require("axios"); const cheerio require("…

Navicat误删除生产环境SQLServer2012单表数据后恢复单表数据

背景&#xff1a; 1-后端更新功能部署到客户生产环境时误将测试环境数据保留&#xff0c;项目负责人发现后告知后端。 2-后端登录客户生产数据库使用navicat删除一张表的单表数据时多删了几条数据&#xff0c;判断弄乱了客户生产环境下自己产生的单表数据。 思路&#xff…

4.14 构建onnx结构模型-Min

前言 构建onnx方式通常有两种&#xff1a; 1、通过代码转换成onnx结构&#xff0c;比如pytorch —> onnx 2、通过onnx 自定义结点&#xff0c;图&#xff0c;生成onnx结构 本文主要是简单学习和使用两种不同onnx结构&#xff0c; 下面以 Min 结点进行分析 方式 方法一&…

算法刷题:最大异或对(Trie树扩展)、食物链(并查集扩展)

目录 引言一、最大异或对&#xff08;Trie树扩展&#xff09;1.题目描述2.解题思路3.代码实现4.测试 二、食物链&#xff08;并查集扩展&#xff09;1.题目描述2.解题思路3.代码实现4.测试 引言 这两个扩展题能够让我们更加的熟悉Trie树和并查集的使用&#xff0c;这两道题可以…

易天成功通过TUV、CB资质认证,品质再获认可

近日&#xff0c;易天喜获佳音&#xff0c;成功通过TUV和CB资质认证&#xff0c;再次彰显了我司在产品质量管理上的不断坚持完善。这一重要认证的取得&#xff0c;是易天在国际标准体系下产品质量的卓越表现&#xff0c;为客户提供更可靠、安全的产品奠定了坚实基础。 TUV资质认…

Python-地图可视化

地图可视化 1.基础地图使用1.1基础地图演示1.2视觉映射器 2.全国疫情地图2.1数据整理2.2创建地图并添加数据2.3设置全局配置 3.省级疫情图 1.基础地图使用 1.1基础地图演示 # 导入模块 from pyecharts.charts import Map # 绘图 map Map() # 构建数据 data [("北京市&…

腾讯云优惠全站搜——云服务器优惠配置大全精准报价

腾讯云推出优惠全站搜页面 https://curl.qcloud.com/PPrF9NFe 在这个页面可以一键查询所需云服务器、轻量应用服务器、数据库、存储、CDN、网络、安全、大数据等云产品优惠活动大全&#xff0c;活动打开如下图&#xff1a; 腾讯云优惠全站搜 如上图&#xff0c;在这个页面可以查…

禁止浏览器记住密码和自动填充 element-ui+vue

vue 根据element-ui 自定义密码输入框&#xff0c;防止浏览器 记住密码和自动填充 <template><divclass"el-password el-input":class"[size ? el-input-- size : , { is-disabled: disabled }]"><inputclass"el-input__inner"…