手记

SpringMVC后台token防重复提交解决方案

思路

1.添加拦截器,拦截需要防重复提交的请求
2.通过注解@Token来添加token/移除token    3.前端页面表单添加(如果是Ajax请求则需要在请求的json数据中添加token值)

核心源码

拦截器源码实现

/**
 * com.xxx.interceptor.TokenInterceptor.java
 * Copyright 2018 Lifangyu, Inc. All rights reserved.
 */package com.xxx.common.interceptor;import org.apache.log4j.Logger;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;import java.util.Random;import java.util.UUID;/**
 * Desc:防重复提交的拦截器
 * <p>
 * Created by lifangyu on 2018/02/27.
 */public class TokenInterceptor extends HandlerInterceptorAdapter {

    Logger logger = Logger.getLogger(TokenInterceptor.class);    static String splitFlag = "_";    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            Token annotation = method.getAnnotation(Token.class);            if (annotation == null) {                return true;
            }            
            boolean needSaveSession = annotation.add();            if (needSaveSession) {
                Random random = new Random();
                String uuid = UUID.randomUUID().toString().replace(splitFlag, String.valueOf(random.nextInt(100000)));
                String tokenValue = String.valueOf(System.currentTimeMillis());
                request.setAttribute("token", uuid + splitFlag + tokenValue);                // session 中 token 的key 每次都是变化的[适应浏览器 打开多个带有token的页面不会有覆盖session的key]
                request.getSession(true).setAttribute(uuid, tokenValue);
            }            boolean needRemoveSession = annotation.remove();            if (needRemoveSession) {                if (isRepeatSubmit(request)) {
                    logger.warn("please don't repeat submit,url:" + request.getServletPath());                    return false;
                }
                String clinetToken = request.getParameter("token");                if (clinetToken != null && clinetToken.indexOf(splitFlag) > -1) {
                    request.getSession(true).removeAttribute(clinetToken.split("_")[0]);
                }
            }            return true;
        } else {            return super.preHandle(request, response, handler);
        }
    }    /**
     * 判断是否是重复提交
     *
     * @param request
     * @return
     */
    private boolean isRepeatSubmit(HttpServletRequest request) {

        String clinetToken = request.getParameter("token");        if (clinetToken == null) {            return true;
        }
        String uuid = clinetToken.split("_")[0];
        String token = clinetToken.split("_")[1];
        String serverToken = (String) request.getSession(true).getAttribute(uuid);        if (serverToken == null) {            return true;
        }        if (!serverToken.equals(token)) {            return true;
        }        return false;
    }
}

注解源码

/**
 * com.xxx.interceptor.Token.java
 * Copyright 2018 Lifangyu, Inc. All rights reserved.
 */package com.xxx.common.interceptor;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/**
 * Desc:Token 注解
 * <p>
 * Created by lifangyu on 2018/02/27.
 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Token {    /**
     * 添加token的开关[true:添加;false:不添加,default:false]
     *
     * @return
     */
    boolean add() default false;    /**
     * 移除token的开关[true:删除;false:不删除,default:false]
     *
     * @return
     */
    boolean remove() default false;

}

拦截器的配置

在springMVC的servlet配置文件中配置拦截器

<!-- 拦截器配置 --><mvc:interceptors>
    <!-- 配置Token拦截器,防止用户重复提交数据 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.xxx.interceptor.TokenInterceptor"/>
    </mvc:interceptor></mvc:interceptors>

使用指南

1.在进入页面的controller方法上添加注解@Token(add=true)

@Token(add = true)@RequestMapping("toXxxHtml")public String toXxxHtml(Model mv) {
   ......   return "xxx/xxxHtml";
}

2.在页面toXxxHtml.html添加

<form id="xxx_submit_form" class="form-horizontal" action="${ctx}/xxx/addXxx.do" method="post">
   ......   <!-- 注:name必须是token -->
   <input type="hidden"  name="token" value="${token!''}"/>
   ......</form>

3.在防止重复提交的controller方法上添加@Token(remove = true)

@Token(remove = true)@RequestMapping("addXxx")public String addXxx() throws Exception {
   ......   return "redirect:toXxxHtml.do";
}

原文出处:https://www.cnblogs.com/yclimb/p/9934106.html

0人推荐
随时随地看视频
慕课网APP