基于Spring Boot防止接口重复提交代码示例
在现代Web应用中,防止接口重复提交是一个常见的需求,尤其是在处理表单提交或敏感操作时。本文将介绍几种在Spring Boot中实现接口防重复提交的方法,并提供相应的代码示例。
1. 使用Token机制
1.1 引入Redis依赖
首先,需要在项目的pom.xml文件中添加Redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1.2 生成Token
在Controller中生成Token并存储在Redis中:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.UUID;
@RestController
public class TokenController {
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("/formByRedis")
public ModelAndView showFormByRedis() {
ModelAndView form = new ModelAndView("form");
String token = UUID.randomUUID().toString();
// 设置过期时间为5分钟
redisTemplate.opsForValue().set(token, token, 5, TimeUnit.MINUTES);
form.addObject("token", token);
return form;
}
}
1.3 传递Token
在表单中添加隐藏字段来传递Token:
<form action="/base/submitByRedis" method="post">
<input type="hidden" name="token" th:value="${token}">
<!-- 其他表单字段 -->
<button type="submit">Submit</button>
</form>
1.4 验证Token
在Controller中验证Token:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@PostMapping("/submitByRedis")
public String handleFormByRedis(@RequestParam String token) {
String redisToken = redisTemplate.opsForValue().get(token);
if (redisToken == null) {
throw new RuntimeException("Duplicate submit detected");
}
// 删除Token
redisTemplate.delete(token);
// 处理表单数据
return "success";
}
2. 使用Spring AOP
Spring AOP可以用来实现切面编程,从而在多个方法中复用防重复提交的逻辑。
2.1 创建切面
创建一个切面类DuplicateSubmitAspect:
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DuplicateSubmitAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Around("@annotation(preventDuplicateSubmit)")
public Object around(ProceedingJoinPoint joinPoint, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
StringBuilder key = new StringBuilder();
// 获取class
String 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());
}
// 判断是否已经请求过
if (redisTemplate.hasKey(key.toString())) {
throw new RuntimeException("请勿重复提交");
}
// 标记请求已经处理过
redisTemplate.opsForValue().set(key.toString(), "1", preventDuplicateSubmit.expireSeconds(), TimeUnit.SECONDS);
return joinPoint.proceed();
}
}
2.2 使用注解
在Controller方法上使用@PreventDuplicateSubmit注解:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AnnotationController {
@PostMapping("/submitByAnnotation")
@PreventDuplicateSubmit
public String handleFormByAnnotation(@RequestParam String param) {
// 处理表单数据
return "success";
}
}
3. 总结
本文介绍了两种在Spring Boot中实现接口防重复提交的方法:使用Token机制和使用Spring AOP。每种方法都有其适用场景和优缺点,可以根据实际需求选择合适的方法。通过这些方法,可以有效防止用户重复提交表单,提高系统的稳定性和用户体验。