用表单进行文件上传的过程中,比较容易发生的就是重复提交,这样会造成很多无效的数据,为此,我的项目中加入了防止重复提交机制。防止重复提交的原理就是,在页面生成的时候,后台会为页面生成一个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;
}
}