springboot防止表单重复提交_Spring Boot 使用 AOP 防止重复提交
- 思路
- Code
- 多线程测试
- 《Java 2019 超神之路》
- 《Dubbo 实现原理与源码解析 —— 精品合集》
- 《Spring 实现原理与源码解析 —— 精品合集》
- 《MyBatis 实现原理与源码解析 —— 精品合集》
- 《Spring MVC 实现原理与源码解析 —— 精品合集》
- 《Spring Boot 实现原理与源码解析 —— 精品合集》
- 《数据库实体设计合集》
- 《Java 面试题 —— 精品合集》
- 《Java 学习指南 —— 精品合集》
“在传统的web项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端。页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性。
上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些大了,本节的实现方案无需前端配合,纯后端处理。
思路
- 自定义注解
@NoRepeatSubmit
标记所有Controller中的提交请求 - 通过AOP 对所有标记了
@NoRepeatSubmit
的方法拦截 - 在业务方法执行前,获取当前用户的 token(或者JSessionId)+ 当前请求地址,作为一个唯一 KEY,去获取 Redis 分布式锁(如果此时并发获取,只有一个线程会成功获取锁)
- 业务方法执行后,释放锁
关于Redis 分布式锁
- 不了解的同学戳这里 ==> Redis分布式锁的正确实现方式
- 使用Redis 是为了在负载均衡部署,如果是单机的部署的项目可以使用一个线程安全的本地Cache 替代 Redis
Code
这里只贴出 AOP 类和测试类,完整代码见 ==> Gitee
@Aspect
@Component
public class RepeatSubmitAspect {private final static Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);@Autowiredprivate RedisLock redisLock;@Pointcut("@annotation(noRepeatSubmit)")public void pointCut(NoRepeatSubmit noRepeatSubmit) {}@Around("pointCut(noRepeatSubmit)")public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {int lockSeconds = noRepeatSubmit.lockTime();HttpServletRequest request = RequestUtils.getRequest();Assert.notNull(request, "request can not null");// 此处可以用token或者JSessionIdString token = request.getHeader("Authorization");String path = request.getServletPath();String key = getKey(token, path);String clientId = getClientId();boolean isSuccess = redisLock.tryLock(key, clientId, lockSeconds);if (isSuccess) {LOGGER.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);// 获取锁成功, 执行进程Object result;try {result = pjp.proceed();} finally {// 解锁redisLock.releaseLock(key, clientId);LOGGER.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);}return result;} else {// 获取锁失败,认为是重复提交的请求LOGGER.info("tryLock fail, key = [{}]", key);return new ResultBean(ResultBean.FAIL, "重复请求,请稍后再试", null);}}private String getKey(String token, String path) {return token + path;}private String getClientId() {return UUID.randomUUID().toString();}}
多线程测试
测试代码如下,模拟十个请求并发同时提交
@Component
public class RunTest implements ApplicationRunner {private static final Logger LOGGER = LoggerFactory.getLogger(RunTest.class);@Autowiredprivate RestTemplate restTemplate;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("执行多线程测试");String url="http://localhost:8000/submit";CountDownLatch countDownLatch = new CountDownLatch(1);ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0; i<10; i++){String userId = "userId" + i;HttpEntity request = buildRequest(userId);executorService.submit(() -> {try {countDownLatch.await();System.out.println("Thread:"+Thread.currentThread().getName()+", time:"+System.currentTimeMillis());ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);System.out.println("Thread:"+Thread.currentThread().getName() + "," + response.getBody());} catch (InterruptedException e) {e.printStackTrace();}});}countDownLatch.countDown();}private HttpEntity buildRequest(String userId) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("Authorization", "yourToken");Map<String, Object> body = new HashMap<>();body.put("userId", userId);return new HttpEntity<>(body, headers);}}
成功防止重复提交,控制台日志如下,可以看到十个线程的启动时间几乎同时发起,只有一个请求提交成功了
来源:http://jianshu.com/p/09860b74658e
- 《Java 2019 超神之路》
- 《Dubbo 实现原理与源码解析 —— 精品合集》
- 《Spring 实现原理与源码解析 —— 精品合集》
- 《MyBatis 实现原理与源码解析 —— 精品合集》
- 《Spring MVC 实现原理与源码解析 —— 精品合集》
- 《Spring Boot 实现原理与源码解析 —— 精品合集》
- 《数据库实体设计合集》
- 《Java 面试题 —— 精品合集》
- 《Java 学习指南 —— 精品合集》
springboot防止表单重复提交_Spring Boot 使用 AOP 防止重复提交相关推荐
- 【一文学会文件上传】SpringBoot+form表单实现文件上传
唠嗑部分 平时我们在项目过程中,往往会遇到这种情况,比如:我的用户应该有一个头像,那就涉及到文件上传,那么文件应该如何存储呢? 这就会有很多方式 1.最简单的就是存在服务器上,这就要考虑到服务器的磁盘 ...
- 用javascript写一个简单的登录表单,包含用户名、密码和手机号码。提交数据时验证用户名、密码和手机号输入格式。
题目:写一个用于登录的表单,包含用户名.密码和手机号码.提交数据的时候,要求验证: 1. 用户名必须输入汉字: 2. 密码总共6位,前2位为字母,后4位为数字: 3. 手机号码符合中国大陆手机号码格式 ...
- springboot 订单重复提交_Spring Boot (一) 校验表单重复提交
一.前言 在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据. 存在如上问题可以交给前端解决,判断多长时间内不能再次点 ...
- springboot防止表单重复提交
springboot和vue使用时网络卡导致请求并发提交 数据请求做了去重处理,但是无法处理重复并发请求 实现代码 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown ...
- SpringBoot+Vue表单文件上传
版权声明:本文首发 http://asing1elife.com ,转载请注明出处. https://blog.csdn.net/asing1elife/article/details/8281181 ...
- 表单验证[用户名、邮箱、密码、重复密码]
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- html表单提交后怎么发送邮箱,Dreamweaver中用表单制作了留言板,如何将内容提交后发到指定邮箱?...
解决思路: 关键点是设置表单的action属性为"mailto:"+email地址. 具体步骤: 1.设置表单的action属性为接收邮件的email地址,并设置表单的MIME编码 ...
- php 表单 同步,Jquery点击按钮 异步和同步提交表单
最近在开发一个jsp学生信息管理系统,由于刚刚接触jsp,遇到问题比较多,特此记录与大家分享. Jquery ajax提交表单到servlet示例 前台部分代码: 姓名 学号 ajax提交表 ...
- form表单下的button不设置type会自动提交表单
有时html页面button点击会出现自动提交表单的情况. 后来查资料得知,form表单下的按钮在没有指定type类型的时候,button会有一个默认的type="submit" ...
- jq html 回车提交表单,jQuery事件之键盘事件(ctrl+Enter回车键提交表单等)
键盘事件处理所有用户在键盘敲击的情况,不管在文本输入区域内部还是外部.键盘事件在不同的浏览器中作用的范围是不一样的,通常这种键盘事件可以作用于 Form元素,a标签元素,window ,documen ...
最新文章
- 我为什么辞去了热爱的 AI 工作?
- code first基础
- python修改linux网卡信息,linux下修改网卡接口名称
- MySQL The password hash doesn't have the expected format.
- slf4j介绍以及实现原理窥探
- Notification的学习,4.0前后的差别,和在设置声音的时候获取资源的uri方法
- 属性面板 脚本_如何在组态王中实现同类型设备公用操作面板的调用---干货
- CVPR 2021 | 姿态可控的语音驱动
- iOS应用商店审核指南
- 使用laypage分页
- everthing 打开历史记录功能
- 深度学习视频教程推荐
- App隐私合规注意事项和相关材料
- Flutter ListView ListView.build ListView.separated
- MNIST手写数字数据集读取方法
- python注释第一行_python第一行注释是什么意思?
- VScode快速创建HTML标签
- python版本切换 for Windows(Anaconda) Linux(pyenv)
- 史上最详细:word文档怎么在第三页插入页码,只要7步
- Textual Entailment(自然语言推理-文本蕴含) - AllenNLP
热门文章
- spring MVC 使用 modelAndView.setViewName(forward:*.action) 发送重定向
- 统计细菌基因组ORF
- opencv5-objdetect之级联分类器
- Shiro权限控制框架
- 安腾机器IA64 全自动网络安装Linux - elilo+kickstart
- centos SSH免密登录
- redis数据类型之String
- Activiti(Flowable)流程克隆(运行时实例克隆)解决方案 一种订单制造中分裂与合并的核心技术实践...
- Jni 返回jstring字符串
- python购物车----运维开发初学