实现原理:
- 自定义防止重复提交标记(@RepeatSubmit)。
- 对需要防止重复提交的Congtroller里的mapping方法加上该注解。
- 新增Aspect切入点,为@RepeatSubmitAspect加入切入点。
- 每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。
- 重复提交时Aspect会判断当前redis是否有该key,若有则拦截。
public enum CaffeineCaches { baseUsers, userRequestUrls(3L, 1000); private int maxSize = 1000; //默认最大缓存数量 private Long ttl = 3600L; //默认过期时间(单位:秒) CaffeineCaches(){ } CaffeineCaches(Long ttl,int maxSize){ this.ttl = ttl; this.maxSize = maxSize; } public int getMaxSize(){ return maxSize; } public Long getTtl(){ return ttl; } }
@Configuration public class CacheConfig { @Bean public CacheManager cacheManager(){ SimpleCacheManager manager = new SimpleCacheManager(); List<CaffeineCache> cacheList = new ArrayList<>(); for(CaffeineCaches ca: CaffeineCaches.values()){ cacheList.add(new CaffeineCache(ca.name(), Caffeine.newBuilder().recordStats() .expireAfterWrite(ca.getTtl(), TimeUnit.SECONDS) .maximumSize(ca.getMaxSize()) .build())); } manager.setCaches(cacheList); return manager; } }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RepeatSubmit { }
import lombok.extern.slf4j.Slf4j; @Slf4j @Aspect @Component public class RepeatSubmitAspect { @Autowired private CacheManager caffeineCacheManager; @Pointcut("@annotation(com.xx.annotation.RepeatSubmit)") public void logPointCut() { } @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { Cache cache = caffeineCacheManager.getCache(CaffeineCaches.userRequestUrls.name()); HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); String ip = IPUtils.getIpAddr(request); String url = request.getServletPath(); String key = ip.replaceAll("\.", "") + "-" + url; SysUser u = (SysUser) SecurityUtils.getSubject().getPrincipal(); log.info(">>>>>>>>>>>>请求ip: {} 请求url: {} 当前用户:{}", ip, url, u != null ? u.getUid() : "anno"); Object result = null; if (u != null && !StringUtils.isEmpty(u.getUid())) { key = key + "-" + u.getUid() + "-" + url; if (cache.get(key) == null ) { result = point.proceed(); cache.put(key, 1); } else { throw new RRException(-91002); } } else { // 匿名 result = point.proceed(); } return result; } }
@RepeatSubmit @PostMapping("/x/x") public R x(@RequestBody x x) { }