spring接口重放过滤问题
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>