本文介绍如何使用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) {
                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";
}

结语

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

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



SpringMVC后台token防重复提交解决方案的更多相关文章

  1. spring MVC 后台token防重复提交解决方案

    看到公司有个部门提出了这个问题,补个粗略的解决方案... 1.编写拦截器 /** * Description: 防止重复提交 * * @Author liam * @Create Date: 2018 ...

  2. (九)Struts2 防重复提交

    所有的学习我们必须先搭建好Struts2的环境(1.导入对应的jar包,2.web.xml,3.struts.xml) 第一节:重复提交示例演示 struts.xml <?xml version ...

  3. AJAX防重复提交的办法总结

    最近的维护公司的一个代理商平台的时候,客服人员一直反映说的统计信息的时候有重复数据,平台一直都很正常,这个功能是最近新进的一个实习生同事写的功能,然后就排查问题人所在,发现新的这个模块的AJAX提交数 ...

  4. struts2学习(15)struts2防重复提交

    一.重复提交的例子: 模拟一种情况,存在延时啊,系统比较繁忙啊啥的. 模拟延迟5s钟,用户点了一次提交,又点了一次提交,例子中模拟这种情况: 这样会造成重复提交:   com.cy.action.St ...

  5. 拦截器springmvc防止表单重复提交【1】

    [参考博客:http://www.cnblogs.com/hdwpdx/archive/2016/03/29/5333943.html] springmvc 用拦截器+token防止重复提交 首先,防 ...

  6. 使用aop注解实现表单防重复提交功能

    原文:https://www.cnblogs.com/manliu/articles/5983888.html 1.这里采用的方法是:使用get请求进入表单页面时,后台会生成一个tokrn_flag分 ...

  7. 浅谈C#在网络波动时防重复提交

    前几天,公司数据库出现了两条相同的数据,而且时间相同(毫秒也相同).排查原因,发现是网络波动造成了重复提交. 由于网络波动而重复提交的例子也比较多: 网络上,防重复提交的方法也很多,使用redis锁, ...

  8. SpringMVC防止表单重复提交

    最近公司上线,有同志进行攻击,表当防重复提交也没有弄,交给我 ,本人以前也没弄过,知道大概的思路,但是那样实在是太麻烦了,虽然后面试过使用过滤器加拦截器实现,不过还是有点小麻烦. 后来在网上搜索后发现 ...

  9. Spring MVC表单防重复提交

    利用Spring MVC的过滤器及token传递验证来实现表单防重复提交. 创建注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RU ...

随机推荐

  1. CSS生僻问题一网打尽

    CSS生僻问题一网打尽 伪类和伪元素 伪类 何为伪类? 像类不是类,不是自己声明的类(不写样式也存在). 对伪元素的认识在早期网页上的超链接.链接(锚啊)用下划线标出来,点击后链接变紫色,鼠标悬上去变 ...

  2. js switch 扩展

    //demo var num=99 switch(n){ case 80<n: document.write("优秀");break; case 70<n: docum ...

  3. 二维码相关工具Qrcode笔记

  4. 关于SVN更新时文件加锁的小结

    今天使用SVN更新应用,出现了下面的问题: update D:/workspace/acode/resource/springconf -r 6622 --force    Attempted to ...

  5. quick-x在windows平台打包加密文件

    D:\quick-cocos2d-x-2.2.1-rc\bin>compile_scripts -i ..\mygames\myth\scripts -o ..\mygames\myth\res ...

  6. Binary Tree Paths 解答

    Question Given a binary tree, return all root-to-leaf paths. For example, given the following binary ...

  7. html系列教程--center dl dt dd div

    <center> 标签:对其所包括的文本进行水平居中. <datalist> 标签:定义列表,与 input 元素配合使用该元素,来定义 input 可能的值 demo: &l ...

  8. Android 开发工具类 32_通过 HTTP 协议实现文件上传

    完成像带有文件的用户数据表单的上传,而且可以上传多个文件,这在用户注册并拍照时尤其有用. import java.io.BufferedReader; import java.io.ByteArray ...

  9. C++ 标准头文件与C头文件区别与联系以及C风格字符串

    1.cstdlib是C++里面的一个常用头文件, 等价于C中的<stdlib.h>. 2.一般一个带“.h” 扩展名的库文件,比如iostream.h.这是延续C语言的,为了兼容C.在新标 ...

  10. mariadb或者mysql查看某个库相关的用户授权信息

    mariadb或者mysql查看某个库相关的授权信息 SELECT * FROM mysql.Db WHERE Db='DB_NAME';