解决表单重复提交问题

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 

    

以上是关于解决表单重复提交问题的主要内容,如果未能解决你的问题,请参考以下文章

token防止前端重复提交

解决表单重复提交

SpringBoot接口+Redis解决用户重复提交问题

常见表单重复提交问题整理及解决方法

bootstrap中模态框提交表单成功后,如果不对网页进行刷新,当再次提交表单时会提交重复数据,求解决

12-struts2防止表单重复提交