美文网首页Java基础Java
SpringMVC后台token防重复提交解决方案

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

作者: YClimb | 来源:发表于2018-03-16 14:29 被阅读907次

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

    本文介绍如何使用token来防止前端重复提交的问题。


    目录

    • 1.思路
    • 2.拦截器源码实现
    • 3.注解源码
    • 4.拦截器的配置
    • 5.使用指南
    • 6.结语

    思路

    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) {
                    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;
            }
            if (clinetToken.indexOf(splitFlag) > -1) {
                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";
    }
    

    结语

    到此本文就结束了,欢迎大家继续关注更多Spring案例。

    扫描下面二维码,关注我的公众号哦!!!


    关注我的公众号

    相关文章

      网友评论

        本文标题:SpringMVC后台token防重复提交解决方案

        本文链接:https://www.haomeiwen.com/subject/xaalqftx.html