原理:在新建页面中Session保存token随机码,当保存时验证,通过后删除,当再次点击保存时由于服务器端的Session中已经不存在了,所有无法验证通过。
1.新建注解
1/** 2 * <p> 3 * 防止重复提交注解,用于方法上<br/> 4 * 在新建页面方法上,设置needSaveToken()为true,此时拦截器会在Session中保存一个token, 5 * 同时需要在新建的页面中添加 6 * <input type="hidden" name="token" value="${token}"> 7 * <br/> 8 * 保存方法需要验证重复提交的,设置needRemoveToken为true 9 * 此时会在拦截器中验证是否重复提交 10 * </p> 11 * @author: chuanli 12 * @date: 2013-6-27上午11:14:02 13 * 14 */ 15@Target(ElementType.METHOD) 16@Retention(RetentionPolicy.RUNTIME) 17public @interface AvoidDuplicateSubmission { 18 boolean needSaveToken() default false; 19 boolean needRemoveToken() default false; 20}
2. 新建拦截器
1/** 2 * <p> 3 * 防止重复提交过滤器 4 * </p> 5 * 6 * @author: chuanli 7 * @date: 2013-6-27上午11:19:05 8 */ 9public class AvoidDuplicateSubmissionInterceptor extends HandlerInterceptorAdapter { 10 private static final Logger LOG = Logger.getLogger(AvoidDuplicateSubmissionInterceptor.class); 11 12 @Override 13 public boolean preHandle(HttpServletRequest request, 14 HttpServletResponse response, Object handler) throws Exception { 15 16 User user = UserUtil.getUser(); 17 if (user != null) { 18 HandlerMethod handlerMethod = (HandlerMethod) handler; 19 Method method = handlerMethod.getMethod(); 20 21 AvoidDuplicateSubmission annotation = method.getAnnotation(AvoidDuplicateSubmission.class); 22 if (annotation != null) { 23 boolean needSaveSession = annotation.needSaveToken(); 24 if (needSaveSession) { 25 request.getSession(false).setAttribute("token", TokenProcessor.getInstance().generateToken()); 26 } 27 28 boolean needRemoveSession = annotation.needRemoveToken(); 29 if (needRemoveSession) { 30 if (isRepeatSubmit(request)) { 31 LOG.warn("please don't repeat submit,[user:" + user.getUsername() + ",url:" 32 + request.getServletPath() + "]"); 33 return false; 34 } 35 request.getSession(false).removeAttribute("token"); 36 } 37 } 38 } 39 return true; 40 } 41 42 private boolean isRepeatSubmit(HttpServletRequest request) { 43 String serverToken = (String) request.getSession(false).getAttribute("token"); 44 if (serverToken == null) { 45 return true; 46 } 47 String clinetToken = request.getParameter("token"); 48 if (clinetToken == null) { 49 return true; 50 } 51 if (!serverToken.equals(clinetToken)) { 52 return true; 53 } 54 return false; 55 } 56 57}
3. 在Spring中配置
1<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> 2 <property name="interceptors"> 3 <list> 4 <bean class="com.sohu.tv.crm.aop.UserLogInterceptor"/> 5 <bean class="com.sohu.tv.crm.aop.AvoidDuplicateSubmissionInterceptor"/> 6 </list> 7 </property> 8 9</bean>
4. 在相关方法中加入注解:
@RequestMapping("/save") @AvoidDuplicateSubmission(needRemoveToken = true) public synchronized ModelAndView save(ExecutionUnit unit, HttpServletRequest request, HttpServletResponse response) throws Exception {
@RequestMapping("/edit") @AvoidDuplicateSubmission(needSaveToken = true) public ModelAndView edit(Integer id, HttpServletRequest request) throws Exception {
5.在新建页面中加入
<input type="hidden" name="token" value="${token}">