之前谈到过前端页面防止重复提交的方法,现在谈谈后端拦截器实现方式。
自定义拦截器实现以下防重原理:
1.初始化页面时生成一个唯一ID,将其放在页面隐藏域和session中2.拦截器拦截请求,校验来自页面请求中的唯一ID与session中的ID是否一致
3.判断,如果一致则提交成功并移除session中的ID,不一致则说明重复提交并记录日志
一、自定义注解
package com.test.sub.repeatsubmit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
boolean save() default false;
boolean remove() default false;
}
二、自定拦截器
package com.test.sub.repeatsubmit;
import java.lang.reflect.Method;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter{
private static final Logger log = Logger.getLogger(RepeatSubmitInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HandlerMethod handlerMethod = null;
try {
handlerMethod = (HandlerMethod)handler;
} catch (Exception e) {
return true;
}
Method method = handlerMethod.getMethod();
Token token = method.getAnnotation(Token.class);
if(token != null ){
boolean saveSession = token.save();
if(saveSession){
request.getSession(true).setAttribute("token", UUID.randomUUID());
}
boolean removeSession = token.remove();
if(removeSession){
if(isRepeatSubmitSession(request)){
log.info("repeat submit session :"+request.getServletPath());
return false;
}
request.getSession(true).removeAttribute("token");
}
}
return true;
}
private boolean isRepeatSubmitSession(HttpServletRequest request){
String sessionToken = String.valueOf(request.getSession(true).getAttribute("token"));
String clientToken = String.valueOf(request.getParameter("token"));
if(sessionToken == null || sessionToken.equals("")){
return true;
}
if(clientToken == null || clientToken.equals("")){
return true;
}
if(!sessionToken.equals(clientToken)){
return true;
}
return false;
}
}
三、初始化加载防重页面,生成唯一ID(代码片段)
/**
* 打开新增或修改页面
* @return
*/
@RequestMapping("showAddPage")
@Token(save=true)
public String showPlanAddPage(String rowId,int param,Model model) {...}
四、页面隐藏域保存ID 或者 ajax提交时加入ID
<!-- 表单提交 -->
<input type="hidden" name="token" value="${token }">
var data=encodeURI("name="+$('#name').val()+"&token=${token}");
$.ajax({
type: "POST",
async:true,
url: "${rootPath}/iminventoryplan/save",
data:data,
success: function(msg){
if(msg.result == 'true' || msg.result == true) {
$.messager.alert('提示',msg.msg,'info');
goBack(1);
}
else {
$.messager.alert('提示',msg.msg,'error');
$('#appsave').linkbutton('enable');
goBack(1);
}
}
});
五、需要防重校验的方法
/**
* 保存新增或修改的记录,将其持久化到数据库中
* @return
*/
@RequestMapping("/save")
@ResponseBody
@Token(remove=true)
public Map<String,String> saveTkiminventoryplanInfo(HttpServletRequest request){...}
六、Spring加入拦截器配置
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="com.test.sub.repeatsubmit.RepeatSubmitInterceptor" ></bean>
</mvc:interceptor>