闽公网安备 35020302035485号
主题:SpringBoot+AOP+Redis 防止重复请求提交
1、实现步骤

<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的访问参数,默认的就行,不需要特别配置。@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
/**
* 加锁过期时间,默认是5秒
* @return
*/
long lockTime() default 5L;
}
这段代码定义了一个Java注解(Annotation)叫做RepeatSubmit。注解是Java提供的一种元数据机制,它可以被用于为代码提供附加的信息,这些信息可以被编译器用于生成代码、生成文档、代码检查等。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 {
@Resource
private RedisTemplate redisTemplate;
@Pointcut("@annotation(repeatSubmit)")
public void pointCutNoRepeatSubmitAspect(RepeatSubmit repeatSubmit) {
}
// 堆代码 duidaima.com
@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),用于处理重复提交的情况。以下是对代码中的主要部分的解释:
/**
* 构建请求签名
* @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、 测试
@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
}
验证通过。