1、定义注释
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface AvoidRepeatRequest {
/** 请求间隔时间,单位秒,该时间范围内的请求为重复请求 */
int intervalTime() default 3;
/** 是否根据参数进行校验 */
boolean checkParameter() default false;
/** 是否根据用户进行校验 */
boolean checkUser() default true;
/** 返回的提示信息 */
String msg() default "请不要频繁重复请求!";
}
2、添加过滤器
import com.alibaba.fastjson.JSON;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description : 重复请求过滤器
* @Author : cxw
* @Date : 2022/11/30 10:56
* @Version : 1.0
**/
@Component
@Aspect
@Order(100)
public class RepeatRequestFilter {
Logger logger= LoggerFactory.getLogger(RepeatRequestFilter.class);
ExpiringMap<String,String> cacheMap = ExpiringMap.builder()
//设置最大值,添加第101个entry时,会导致第1个立马过期(即使没到过期时间)
.maxSize(100000)
//设置每个key有效时间60s,如果key不设置过期时间,key永久有效
.expiration(60, TimeUnit.SECONDS)
//允许更新过期时间值,如果不设置variableExpiration,不允许后面更改过期时间,一旦执行更改过期时间操作会抛异常UnsupportedOperationException
.variableExpiration()
//CREATED:只在put和replace方法清零过期时间
//ACCESSED:在CREATED策略基础上增加 在还没过期时get方法清零过期时间。
//清零过期时间也就是重置过期时间,重新计算过期时间
.expirationPolicy(ExpirationPolicy.CREATED)
.build();
private static final String SUFFIX = "C_";
// 定义 注解 类型的切点
@Pointcut("@annotation(com..api.common.annotation.AvoidRepeatRequest)")
public void arrPointcut() {}
// 实现过滤重复请求功能
@Around("arrPointcut()")
public Object arrBusiness(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取 redis key,由 session ID 和 请求URI 构成
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = sra.getRequest();
String key = SUFFIX + "_" + request.getRequestURI();
// 获取方法的 AvoidRepeatRequest 注解
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
AvoidRepeatRequest arr = method.getAnnotation(AvoidRepeatRequest.class);
if(arr!=null&&arr.checkUser()){
key+="_"+ request.getSession().getId();
}
if(arr!=null&&arr.checkParameter()){
Map<String, Object> nameAndValue = ParameterNameUtils.getNameAndValue(joinPoint);
if(nameAndValue!=null&&nameAndValue.size()>0){
key+="_"+ JSON.toJSONString(nameAndValue);
}
}
// 判断是否是重复的请求
if (arr!=null&&continceKey(key,arr.intervalTime())) {
throw new Exception("提示:"+arr.msg());
}
return joinPoint.proceed();
}
/**
* 验证
* @param key
* @param intervalTime
* @return
*/
private boolean continceKey(String key, int intervalTime) {
String s = cacheMap.get(key);
cacheMap.put(key,"v",intervalTime,TimeUnit.SECONDS);
if(s!=null){
return true;
}
return false;
}
}
3、使用
@AvoidRepeatRequest(intervalTime = 30, msg = "不允许重复提交",checkParameter = true,checkUser = false)
4、引用
<!-- 过期map -->
<dependency>
<groupId>net.jodah</groupId>
<artifactId>expiringmap</artifactId>
<version>0.5.9</version>
</dependency>