解决表单重复提交问题
Posted kuailefangyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决表单重复提交问题相关的知识,希望对你有一定的参考价值。
防止表单重复提交
一、问题背景
现管理平台退货功能,支持上传退货excel并往退货订单表插入退货订单记录功能。运营同事反馈出现上传退货excel时,退货订单表出现重复退货记录,即存在表单重复提交的问题。
由于历史原因,该退货订单表refundOrderId非唯一索引,无法从数据库层面解决该问题,只能从前后端代码解决表单重复提交的问题。
二、解决方案
避免表单重复提交问题,前端表单提交时,可以在点击表单提交按钮后将按钮置灰,也就是禁止用户再次点击,实现方案如下:
<input type="submit" value="提交" οnclick="javascript:document.form.submit();this.disabled=true;" />
注意:禁用表单提交按钮只能在前端防范用户表单重复提交,无法防止恶意通过接口的方式发起重复提交,更安安全的方案也就是token方案来解决该问题。
以下介绍token实现避免表单重复提交的方案:
-
进入表单页面前,后台先生成token并存至缓存,注意该token全局唯一
-
表单提交时带上该token
-
后台校验缓存是否存在token
1、若存在则说明token合法,非重复提交,放行并从缓存中删除该token;
2、若缓存不存在该token则说明系非法表单提交或者重复表单提交,拒绝。
具体实现时,可以针对生成token、校验token实现一个专门的@SubmitToken注解,代码如下:
/**
* 提交token
*/
@Target(ElementType.METHOD, ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SubmitToken
/**
* 创建token
* @return
*/
boolean create() default false;
/**
* 校验token
* @return
*/
boolean check() default false;
针对该token注解,基于AOP拦截该注解并生成或校验token,代码如下:
/**
* 拦截重复表单提交请求
*/
@Slf4j
@Aspect
@Service
public class NoRepeatSubmitAop
@Autowired
private GeneralNearCache generalNearCache;
public NoRepeatSubmitAop()
log.info("防止重复表单提交切面启动");
/**
* 定义切点
*/
@Pointcut("@annotation(com.chinaums.xxx.web.mgrframework.annotation.SubmitToken)")
public void repeatSubmitPtCut()
/**
* 环绕通知处理
*/
@Around("repeatSubmitPtCut()")
public Object checkRepeatSubmit(ProceedingJoinPoint pjp) throws Throwable
HttpServletRequest request = null;
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null)
request = attributes.getRequest();
if(request != null)
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
SubmitToken submitTokenAnnotation = methodSignature.getMethod().getAnnotation(SubmitToken.class);
if(submitTokenAnnotation.create())
String token = UUIDGenerator.getUUID();
request.setAttribute(WebConstant.SUBMIT_TOKEN, token);
generalNearCache.putToCache(token, token);
else if(submitTokenAnnotation.check())
// 直接放行get请求
if(StringUtils.equals(request.getMethod(), RequestMethod.GET.toString()))
return pjp.proceed();
String reqToken = request.getParameter(WebConstant.SUBMIT_TOKEN);
String token = generalNearCache.getFromCache(reqToken);
if(StringUtils.isNotBlank(reqToken) && StringUtils.equals(reqToken, token))
generalNearCache.removeFromCache(reqToken);
return pjp.proceed();
throw new RepeatFormSubmitException(ResultBean.REPEAT_SUBMIT_FAIL, "重复表单请求");
return pjp.proceed();
在跳转到表单提交页面之前,后端controller加上@SubmitToken(create = true)生成token注解:
@SubmitToken(create = true)
@RequestMapping(value = "/goSubmitForm.do", method = RequestMethod.GET)
public String goSubmitForm()
return "submitForm";
页面表单提交时带上token
<input type="submit" value="提交" οnclick="formSubmit(this);" />
对应的js代码:
function formSubmit(e)
var action = document.form.action + "?submit_token="+$("#submit_token").val();
document.form.action=action;
document.form.submit();
e.disabled=true;
后端controller加上@SubmitToken(check=true)校验token注解:
@RequestMapping(value = "/submitForm.do", method = RequestMethod.POST)
@OperateLog(operType = DataConstant.OPER_TYPE_APPROVE)
@SubmitToken(check = true)
public void submitForm(MultipartFile uploadFile, HttpServletRequest request,
HttpServletResponse response) throws Exception
以上是关于解决表单重复提交问题的主要内容,如果未能解决你的问题,请参考以下文章