本文介绍如何使用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. (亿级流量)分布式防重复提交token设计

    大型互联网项目中,很多流量都达到亿级.同一时间很多的人在使用,而每个用户提交表单的时候都可能会出现重复点击的情况,此时如果不做好控制,那么系统将会产生很多的数据重复的问题.怎样去设计一个高可用的防重复 ...

  3. 架构设计 | 接口幂等性原则,防重复提交Token管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产 ...

  4. (九)Struts2 防重复提交

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. java AOP使用配置项来进行注入实践

    java AOP使用注解@annotation方式实践 场景: 在目标方法前面和后面执行通知方法 目标类 @Component public class Play { public void watc ...

  2. .NET面试题系列(十三)Lucene底层原理

    索引原理 全文检索技术由来已久,绝大多数都基于倒排索引来做,曾经也有过一些其他方案如文件指纹.倒排索引,顾名思义,它相反于一篇文章包含了哪些词,它从词出发,记载了这个词在哪些文档中出现过,由两部分组成 ...

  3. 3 快速创建SpringBoot项目

    一.Intellij IDEA 创建Spring Boot项目 1.创建工程  2.选择Spring Initializr 3.设置Maven版本管理参数  4.选择引用模块  5.命名工程名 6.选 ...

  4. 二、主目录 Makefile 分析(3)

    2.8 编译选项 重新回到 主 makefile 中执行 2.8.1 u-boot  的编译顺序 代码166 到 189 行,这些是 u-boot 文件编译的顺序,由代码可以看到,首先是从cpu/$( ...

  5. scapy官方文档

    https://thepacketgeek.com/scapy-p-04-looking-at-packets/ http://biot.com/capstats/bpf.html   filter语 ...

  6. js 获取属性名称

    $(function ()        {            myfun();        })        function myfun()        {            var ...

  7. string字符串js操作

    String 对象方法 方法 描述 anchor() 创建 HTML 锚. big() 用大号字体显示字符串. blink() 显示闪动字符串. bold() 使用粗体显示字符串. charAt() ...

  8. HTML5 CSS JavaScript在网页中扮演的角色

    HTML (content layer) CSS (presentation layer) JavaScript (Interactive layer) 参考链接: [1] https://www.y ...

  9. 手动创建binary log files和手动编辑binary log index file会有什么影响

    基本环境:官方社区版MySQL 5.7.19 一.了解Binary Log结构 1.1.High-Level Binary Log Structure and Contents • Binlog包括b ...

  10. [转]gcc -ffunction-sections -fdata-sections -Wl,–gc-sections 参数详解

    背景 有时我们的程序会定义一些暂时使用不上的功能和函数,虽然我们不使用这些功能和函数,但它们往往会浪费我们的ROM和RAM的空间.这在使用静态库时,体现的更为严重.有时,我们只使用了静态库仅有的几个功 ...