后端--防重复提交策略方法

后端–防重复提交策略方法

原因:

前台操作的抖动,快速操作,网络通信或者后端响应慢,都会增加后端重复处理的概率。
情形

  1. 由于用户误操作,多次点击表单提交按钮。
  2. 由于网速等原因造成页面卡顿,用户重复刷新提交页面。
  3. 黑客或恶意用户使用postman等工具重复恶意提交表单(攻击网站)。
  4. 这些情况都会导致表单重复提交,造成数据重复,增加服务器负载,严重甚至会造成服务器宕机。因此有效防止表单重复提交有一定的必要性。

解决方案:

一:给数据库增加唯一键约束
navicat在这里插入图片描述
这种方法需要在代码中加入捕捉插入数据异常:

 try {
	            // insert
	        } catch (DuplicateKeyException e) {
	            logger.error("user already exist");
	        }

二:使用AOP自定义切入实现
实现原理:

1.自定义防止重复提交标记(@AvoidRepeatableCommit)。   
2.对需要防止重复提交的Congtroller里的mapping方法加上该注解。   
3.新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。 
4.每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。   
5.重复提交时Aspect会判断当前redis是否有该key,若有则拦截。
/**
		 * 避免重复提交
		 */
		@Target(ElementType.METHOD)
		@Retention(RetentionPolicy.RUNTIME)
		public @interface AvoidRepeatableCommit {
		
		    /**
		     * 指定时间内不可重复提交,单位秒
		     * @return
		     */
		    long timeout()  default 3 ;
		
		}
/**
		 * 重复提交aop 
		 */
		@Aspect
		@Component
		public class AvoidRepeatableCommitAspect {
		
		    @Autowired
		    private RedisTemplate redisTemplate;
		
		    /**
		     * @param point
		     */
		    @Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)")
		    public Object around(ProceedingJoinPoint point) throws Throwable {
		
		        HttpServletRequest request  = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
		        String ip = IPUtil.getIP(request);
		        //获取注解
		        MethodSignature signature = (MethodSignature) point.getSignature();
		        Method method = signature.getMethod();
		        //目标类、方法
		        String className = method.getDeclaringClass().getName();
		        String name = method.getName();
		        String ipKey = String.format("%s#%s",className,name);
		        int hashCode = Math.abs(ipKey.hashCode());
		        String key = String.format("%s_%d",ip,hashCode);
		        log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
		        AvoidRepeatableCommit avoidRepeatableCommit =  method.getAnnotation(AvoidRepeatableCommit.class);
		        long timeout = avoidRepeatableCommit.timeout();
		        if (timeout < 0){
                                //过期时间5分钟
		            timeout = 60*5;
		        }
		        String value = (String) redisTemplate.opsForValue().get(key);
		        if (StringUtils.isNotBlank(value)){
		            return "请勿重复提交";
		        }
		        redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS);
		        //执行方法
		        Object object = point.proceed();
		        return object;
		    }
		
		}

版权声明:本文为God_00原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。