防止表单重复提交

用表单进行文件上传的过程中,比较容易发生的就是重复提交,这样会造成很多无效的数据,为此,我的项目中加入了防止重复提交机制。防止重复提交的原理就是,在页面生成的时候,后台会为页面生成一个token,前端在上传的时候将token值传给后台,后台实现上传后就会销毁token,如果前端还是用token来上传,就会被判定是重复提交。具体步骤和实现如下:

定义SpringMVC注解

用注解实现后台防止重复提交的开关:

/**
 * 
 * 防止重复提交注解,用于方法上
 * 在新建页面方法上,设置usingToken()为true,此时拦截器会在Session中保存一个token,
 * 同时需要在新建的页面中添加
 * <input type="hidden" name="token" value="${token}">
 * 保存方法需要验证重复提交的,设置needRemoveToken为true
 * 此时会在拦截器中验证是否重复提交
 * 
 * @author 王文路
 * @date 2015-1-9
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidDuplicateSubmission {
    boolean usingToken() default false;

}

使用注解

在需要使用的上传接口上使用以上定义的注解,设置usingToken为true:

@AvoidDuplicateSubmission(usingToken=true)
@RequestMapping(value = "/api/{sessionid}/documents", method = RequestMethod.POST)
 public @ResponseBody
 ResultInfo<List<Document>> upload(@PathVariable("sessionid")String sessionID)

生成Token

在后台返回View的时候,生成Token,并且放在session中,可以让前端拿到token进行使用,后台代码:

@Controller
public class MainViewController extends MultiActionController {

    @RequestMapping (value= "mainview")
    public @ResponseBody
    ModelAndView main(HttpServletRequest request,
            HttpServletResponse response){
        String moduleid = request.getParameter("moduleid");

         String tokent = TokenHelper.getInstance().makeToken();
         request.getSession().setAttribute("token", tokent);

        HttpSession http_session = request.getSession();

        ModelAndView ret = new ModelAndView("main");
        ret.addObject("moduleid", moduleid);
        return  ret;
    }
}

前端使用Token

前端保留Token值

String token =  request.getSession().getAttribute("token");

在上传的时候使用token

<form action=""  enctype="multipart/form-data" method= "post">
        <input type="text" class="form-control" placeholder="动态内容" name ="data">
        <input type="file" name="files" class="btn" multiple style="display:block;">
        <input type="file" name="token" value="<%=token%>">
        <button type="button" id="activty-person-publish-submit" class="btn btn-primary">发表</button>
</form>

设置拦截器

具体实现的处理方法还需要放在拦截器中去处理,拦截器能够拦截所有发向后台的HTTP请求,只要http请求对应的方法上使用了注解并且设置了

usingToken = true

就进行处理。首先从前端参数中取出token,进行比对,看token是否已经失效,如果失效就判定是二次提交,直接拦截。

/**
 *  防止重复提交过滤器
 * 
 * @author 王文路
 * @date 2015-1-9
 */
public class AvoidDuplicateSubmissionInterceptor extends HandlerInterceptorAdapter {

private static final Logger logger = Logger.getLogger(AvoidDuplicateSubmissionInterceptor.class);

@Override
public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {

     AvoidDuplicateSubmission annotation;
    try{
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        annotation = method.getAnnotation(AvoidDuplicateSubmission.class);
    }catch(ClassCastException e){
        return true;
    }

        if (annotation != null) {
            boolean usingToken = annotation.usingToken();

            if (usingToken) {

                if (isRepeatSubmit(request)) {
                    logger.warn("不能重复提交请求,[url:" + request.getServletPath() + "]");
                    return false;
                }
                request.getSession().removeAttribute("token");
            }
        }

    return true;
}

private boolean isRepeatSubmit(HttpServletRequest request) {
    String serverToken = (String) request.getSession().getAttribute("token");
    if (serverToken == null) {
        return true;
    }
    String clinetToken = request.getParameter("token");
    if (clinetToken == null) {
        return true;
    }
    if (!serverToken.equals(clinetToken)) {
        return true;
    }
    return false;
}

}
本站总访问量