防止form表单提交的几种方式

1. 背景介绍

在开发项目中肯可能会出现如下情况:

     1. 用户的失误操作,多次点击表单提交按钮

     2. 由于网速等原因造成页面卡顿,用户重复刷新提交页面

     3. 黑客或恶意用户使用postman等工具重复恶意提交表单

....

这些情况都会导致表单的重复提交,导致数据重复,增加服务器的压力,甚至会造成服务器宕机,因此要有效防止表单重复提交非常必要。

2. 解决方案

2.1 点击一次之后,按钮失效(不推荐,用户刷新页面仍能重复提交)

      通过js代码,当用户点击提交按钮后,屏蔽提交按钮(使按钮无发点击提交或点击无效disabled),从而实现防止表单重复提交。

2.2 用redirect来解决重复提交问题

   简而言之,表单提交后重定向到提交成功的一个页面。

2.3在数据库里添加约束

    在数据库里添加唯一约束或创建唯一索引,防止出现重复数据。简单粗暴的方法。

2.4 在session中存放一个特殊标志(推荐)

     类似于“令牌”机制。当表单页面第一次被请求时,生成一个特殊的字符标志串,存在session中,同时放在表单的隐藏域里。接受处理表单数据时,检查标识字串是否存在,若标志串相同则处理表单提交并立即从session中删除它。若不一致就是重复提交了则忽略这次提交。如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交。

(解决struts2重复提交,可以结合s:token标签解决重复提交问题)

2.5 使用AOP自定义切入实现

实现原理:

  1. 自定义防止重复提交标记(@AvoidRepeatableCommit)。
  2. 对需要防止重复提交的Congtroller里的mapping方法加上该注解。
  3. 新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。
  4. 每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。
  5. 重复提交时Aspect会判断当前redis是否有该key,若有则拦截。

自定义标签

import java.lang.annotation.*;
        
        /**
         * 避免重复提交
         * @author dsx
         * @version
         * @since
         */
        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface AvoidRepeatableCommit {
        
            /**
             * 指定时间内不可重复提交,单位毫秒
             * @return
             */
            long timeout()  default 30000 ;
        
        }

自定义切面Aspect

 /**
         * 解决重复提交aop
         * @author dsx
         * @version 
         * @since 
         */
        @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;
            }
        
        }

如有不足,望不吝赐教!!!

发布了91 篇原创文章 · 获赞 1 · 访问量 3207

猜你喜欢

转载自blog.csdn.net/duan196_118/article/details/104170199