赞
踩
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自定义切入实现
实现原理:
自定义标签
- 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;
- }
-
- }
如有不足,望不吝赐教!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。