当前位置: 首页 > news >正文

Spring Boot 接口防重复提交解决方案

文章目录

    • 前言
    • 使用Token机制
      • 实现步骤
        • 1.生成Token
        • 2.传递Token
        • 3.验证Token
    • 使用Redis
      • 实现步骤
        • 1.引入Redis依赖
        • 2.生成Token
        • 3.传递Token
        • 4.验证Token
    • 使用Spring AOP
      • 实现步骤
        • 1.定义注解
        • 2.创建切面
        • 3.使用注解
    • 总结

前言

在Web开发中,防止用户重复提交表单是一个常见的需求。用户可能会因为网络延迟、误操作等原因多次点击提交按钮,导致后台接收到多个相同的请求。这不仅会浪费服务器资源,还可能导致数据不一致等问题。本文将介绍几种在Spring Boot中实现接口防重复提交的方法。
在这里插入图片描述

使用Token机制

Token机制是一种常见的防重复提交方法。具体步骤如下:
生成Token:用户每次请求表单页面时,服务器生成一个唯一的Token,并将其存储在Session中。
传递Token:将Token嵌入到表单中,随表单一起提交。
验证Token:服务器接收到请求后,首先验证Token是否有效,如果有效则继续处理请求,并从Session中移除该Token;如果无效,则返回错误信息。

实现步骤

1.生成Token

在Controller中生成Token并存储在Session中:

/*** form* @param session* @author senfel* @date 2024/11/12 11:29* @return org.springframework.web.servlet.ModelAndView*/
@GetMapping("/form")
public ModelAndView showForm(HttpSession session) {ModelAndView form = new ModelAndView("form");String token = UUID.randomUUID().toString();session.setAttribute("token", token);form.addObject("token", token);return form;
}
2.传递Token

在表单中添加隐藏字段来传递Token:

<form action="/base/submit" method="post"><input type="hidden" name="token" th:value="${token}"><!-- 其他表单字段 --><button type="submit">Submit</button>
</form>
3.验证Token

在Controller中验证Token:

/*** handleForm* @param token* @param session* @author senfel* @date 2024/11/12 11:34* @return java.lang.String*/
@PostMapping("/submit")
public String handleForm(@RequestParam String token, HttpSession session) {String sessionToken = (String) session.getAttribute("token");if (sessionToken == null || !sessionToken.equals(token)) {throw new RuntimeException("Duplicate submit detected");}// 移除Tokensession.removeAttribute("token");// 处理表单数据return "success";
}

使用Redis

Redis是一个高性能的键值存储系统,可以用来存储和验证Token。具体步骤如下:
生成Token:用户每次请求表单页面时,服务器生成一个唯一的Token,并将其存储在Redis中。
传递Token:将Token嵌入到表单中,随表单一起提交。
验证Token:服务器接收到请求后,首先验证Token是否存在于Redis中,如果存在则继续处理请求,并从Redis中删除该Token;如果不存在,则返回错误信息。

实现步骤

1.引入Redis依赖

在 pom.xml 中添加Redis依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.生成Token

在Controller中生成Token并存储在Redis中:

@Autowired
private StringRedisTemplate redisTemplate;/*** formByRedis* @author senfel* @date 2024/11/12 11:50* @return org.springframework.web.servlet.ModelAndView*/
@GetMapping("/formByRedis")
public ModelAndView showFormByRedis() {ModelAndView form = new ModelAndView("form");String token = UUID.randomUUID().toString();// 设置过期时间redisTemplate.opsForValue().set(token, token, 5, TimeUnit.MINUTES);form.addObject("token", token);return form;
}
3.传递Token

在表单中添加隐藏字段来传递Token:

<form action="/base/submitByRedis" method="post"><input type="hidden" name="token" th:value="${token}"><!-- 其他表单字段 --><button type="submit">Submit</button>
</form>
4.验证Token

在Controller中验证Token:

/*** submitByRedis* @param token* @author senfel* @date 2024/11/12 11:50* @return java.lang.String*/
@PostMapping("/submitByRedis")
public String handleFormByRedis(@RequestParam String token) {String redisToken = redisTemplate.opsForValue().get(token);if (redisToken == null) {throw new RuntimeException("Duplicate submit detected");}// 删除TokenredisTemplate.delete(token);// 处理表单数据return "success";
}

使用Spring AOP

Spring AOP(Aspect-Oriented Programming)可以用来实现切面编程,从而在多个方法中复用防重复提交的逻辑。

实现步骤

1.定义注解

创建一个自定义注解 @PreventDuplicateSubmit:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** PreventDuplicateSubmit* @author senfel* @date 2024/11/12 11:56*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicateSubmit {/**重复请求时间*/int expireSeconds() default 10;
}
2.创建切面

创建一个切面类 DuplicateSubmitAspect:

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.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;/*** DuplicateSubmitAspect* @author senfel* @version 1.0* @date 2024/11/12 11:57*/
@Slf4j
@Aspect
@Component
public class DuplicateSubmitAspect {protected static final Logger logger = LoggerFactory.getLogger(DuplicateSubmitAspect.class);@Autowiredprivate StringRedisTemplate redisTemplate;/*** around* @param joinPoint* @author senfel* @date 2024/11/12 15:45* @return java.lang.Object*/@Around("@annotation(com.example.ccedemo.aop.PreventDuplicateSubmit)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {StringBuilder key = new StringBuilder();//获取classString simpleName = joinPoint.getTarget().getClass().getSimpleName();key.append(simpleName);// 获取请求方法MethodSignature signature=(MethodSignature)joinPoint.getSignature();Method method = signature.getMethod();String methodName = method.getName();key.append(":").append(methodName);//获取请求参数Object[] args=joinPoint.getArgs();for (Object arg : args) {key.append(":").append(arg.toString());}//TODO 获取客户端IP// 获取注解信息PreventDuplicateSubmit annotation = method.getAnnotation(PreventDuplicateSubmit.class);// 判断是否已经请求过if(redisTemplate.hasKey(key.toString())){throw new RuntimeException("请勿重复提交");}//标记请求已经处理过redisTemplate.opsForValue().set(key.toString(),"1",annotation.expireSeconds(), TimeUnit.SECONDS);return joinPoint.proceed();}
}
3.使用注解

在Controller方法上使用 @PreventDuplicateSubmit 注解:

/*** handleFormByAnnotation* @param param* @author senfel* @date 2024/11/12 11:59* @return java.lang.String*/
@PostMapping("/submitByAnnotation")
@PreventDuplicateSubmit
public String handleFormByAnnotation(@RequestParam String param) {// 处理表单数据return "success";
}

总结

本文介绍了三种在Spring Boot中实现接口防重复提交的方法:使用Token机制、使用Redis和使用Spring AOP。每种方法都有其适用场景和优缺点,可以根据实际需求选择合适的方法。通过这些方法,可以有效防止用户重复提交表单,提高系统的稳定性和用户体验。


http://www.mrgr.cn/news/72299.html

相关文章:

  • ABAP开发学习——权限控制
  • Numpy入门及进阶(三)
  • 深入解析 CentOS 7 上 MySQL 8.0 的最佳实践20241112
  • C++ 关于基于范围的for循环(C++11) 语法 详解
  • 【Python】开心消消乐”简化版源码解析:用Python实现消除游戏基础逻辑
  • 企业级大数据安全架构
  • 办公必备:非常好用的截图软件-snipaste
  • Spring Boot 集成 RabbitMQ:消息生产与消费详解
  • 【comfyui教程】ComfyUI学习笔记——最细ComfyUI安装教程!
  • OCX控件注册 SynCardOcx.ocx IE浏览器身份识别
  • DWARF
  • springboot企业信息管理系统,计算机毕业设计项目源码310,计算机毕设程序(LW+开题报告、中期报告、任务书等全套方案)
  • 「QT」基础数据类 之 QString 字符串类
  • 基于正则化算法的SAR图像去噪matlab仿真
  • Spring框架之中介者模式 (Mediator Pattern)
  • SSH远程连接工具详解
  • CLion配置QT开发环境
  • javaSpringbootmsyql智慧园区管理系统的开发88160-计算机毕业设计项目选题推荐(附源码)
  • D3入门:学习思维导图 + 99个中文API详解
  • SpringBoot开发——整合 apache fileupload 轻松实现文件上传与下载
  • js三大组成部分
  • AI文献搜索工具:Lumina
  • 绿色未来之光:光伏发电的优缺点
  • git切换分支的时候,该分支内容被带到另一被切换分支!!!!
  • 深入理解 cached plan must not change result type 原理及解决办法
  • 使用Python查询数据库并生成报告的全流程指南