利用自定义注解+aop+redis防止重复提交
程序员文章站
2022-05-31 17:33:33
...
项目开发一个比较常见的需求就是防止重复提交,一般来说前端可以通过将提交按钮置灰等操作达到目的,但这个方案仍旧有一些缺陷,所以最好由后端来做控制。本文笔者将用自定义注解加redis和aop来实现。
1 、我的项目是springboot项目,首先添加redis和aspectj的依赖。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2 、添加自定义注解,之后想让哪一个方法防止重复提交,只需要在这个方法上加上这个自定义注解便可。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidDuplicateSubmit {
/**
* 指定时间内不可重复提交,单位毫秒
* @return
*/
long timeout() default 5000;
}
3 、添加处理注解的切面类
@Component
@Aspect // 定义切面类
public class AvoidDuplicateSubmitAspect {
@Autowired
private RedisTemplate redisTemplate;
@Around("@annotation(com.chengxi.examples.annotation.AvoidDuplicateSubmit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取request对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = Iputil.getIp(request);
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String className = method.getDeclaringClass().getName();
String methodName = method.getName();
String ipKey = String.format("%s#%s", className, methodName);
int hashCode = Math.abs(ipKey.hashCode());
// 拼接redisKey,如:127.0.0.1_1898984393
String redisKey = String.format("%s_%d", ip, hashCode);
String value = (String) redisTemplate.opsForValue().get(redisKey);
if (!StringUtils.isEmpty(value)) {
return ReturnJson.fail("请勿重复提交");
}
// 获取注解
AvoidDuplicateSubmit avoidDuplicateSubmit = method.getAnnotation(AvoidDuplicateSubmit.class);
long timeout = avoidDuplicateSubmit.timeout();
if (timeout < 0) {
timeout = 5000;
}
// 第一次提交,插入redis
redisTemplate.opsForValue().set(redisKey, UUID.randomUUID().toString(), timeout, TimeUnit.MICROSECONDS);
// 继续执行方法
return joinPoint.proceed();
}
}
获取ip的Iputil:
public class Iputil {
public static String getIp(HttpServletRequest request) {
String unknown = "unknown";
if (request == null) {
return unknown;
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
其中,我们利用自定义注解@AvoidDuplicateSubmit 标记了需要防止重复提交的方法,用户请求该方法时,利用Spring Aop来捕获相关的信息。如类名,方法名,以及ip,通过这三个信息,按一定规则组合成一个key,到redis里去查询是否存在值。若存在,说明规定时间内已经提交过一次,所以返回错误信息。否则则将该key存入redis当中,并且利用过期时间来设置防重复提交的间隔,然后继续执行用户请求的方法。
上一篇: C++手动开启编译优化
下一篇: zipfile模块解压zip文件中文乱码