精华内容
下载资源
问答
  • 2021-03-14 03:48:13

    具体的做法:

    1、获取用户填写用户名和密码的页面时向后台发送一次请求,这时后台会生成唯一的随机标识号,专业术语称为Token(令牌)。

    2、将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端。

    3、服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

    看具体的范例:

    1.创建FormServlet,用于生成Token(令牌)和跳转到form.jsp页面

    importjava.io.IOException;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;public class FormServlet extendsHttpServlet {private static final long serialVersionUID = -884689940866074733L;public voiddoGet(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException {

    String token= UUID.randomUUID().toString() ;//创建令牌

    System.out.println("在FormServlet中生成的token:"+token);

    request.getSession().setAttribute("token", token); //在服务器使用session保存token(令牌)

    request.getRequestDispatcher("/form.jsp").forward(request, response);//跳转到form.jsp页面

    }public voiddoPost(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException {

    doGet(request, response);

    }

    }

    2.在form.jsp中使用隐藏域来存储Token(令牌)

    form表单

    ">

    --%>

    用户名:

    3.DoFormServlet处理表单提交

    importjava.io.IOException;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;public class DoFormServlet extendsHttpServlet {public voiddoGet(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException {boolean b = isRepeatSubmit(request);//判断用户是否是重复提交

    if(b==true){

    System.out.println("请不要重复提交");return;

    }

    request.getSession().removeAttribute("token");//移除session中的token

    System.out.println("处理用户提交请求!!");

    }/*** 判断客户端提交上来的令牌和服务器端生成的令牌是否一致

    *@paramrequest

    *@return* true 用户重复提交了表单

    * false 用户没有重复提交表单*/

    private booleanisRepeatSubmit(HttpServletRequest request) {

    String client_token= request.getParameter("token");//1、如果用户提交的表单数据中没有token,则用户是重复提交了表单

    if(client_token==null){return true;

    }//取出存储在Session中的token

    String server_token = (String) request.getSession().getAttribute("token");//2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单

    if(server_token==null){return true;

    }//3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单

    if(!client_token.equals(server_token)){return true;

    }return false;

    }public voiddoPost(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException {

    doGet(request, response);

    }

    }

    方案二:判断请求url和数据是否和上一次相同

    推荐,非常简单,页面不需要任何传入,只需要在验证的controller方法上写上自定义注解即可

    写好自定义注解

    importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/*** 一个用户 相同url 同时提交 相同数据 验证

    *@authorAdministrator

    **/@Target(ElementType.METHOD)

    @Retention(RetentionPolicy.RUNTIME)public @interfaceSameUrlData {

    }

    写好拦截器

    importjava.lang.reflect.Method;importjava.util.HashMap;importjava.util.Map;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;importcom.thinkgem.jeesite.common.mapper.JsonMapper;/*** 一个用户 相同url 同时提交 相同数据 验证

    * 主要通过 session中保存到的url 和 请求参数。如果和上次相同,则是重复提交表单

    *@authorAdministrator

    **/

    public class SameUrlDataInterceptor extendsHandlerInterceptorAdapter{

    @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throwsException {if (handler instanceofHandlerMethod) {

    HandlerMethod handlerMethod=(HandlerMethod) handler;

    Method method=handlerMethod.getMethod();

    SameUrlData annotation= method.getAnnotation(SameUrlData.class);if (annotation != null) {if(repeatDataValidator(request))//如果重复相同数据

    return false;else

    return true;

    }return true;

    }else{return super.preHandle(request, response, handler);

    }

    }/*** 验证同一个url数据是否相同提交 ,相同返回true

    *@paramhttpServletRequest

    *@return

    */

    public booleanrepeatDataValidator(HttpServletRequest httpServletRequest)

    {

    String params=JsonMapper.toJsonString(httpServletRequest.getParameterMap());

    String url=httpServletRequest.getRequestURI();

    Map map=new HashMap();

    map.put(url, params);

    String nowUrlParams=map.toString();//

    Object preUrlParams=httpServletRequest.getSession().getAttribute("repeatData");if(preUrlParams==null)//如果上一个数据为null,表示还没有访问页面

    {

    httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);return false;

    }else//否则,已经访问过页面

    {if(preUrlParams.toString().equals(nowUrlParams))//如果上次url+数据和本次url+数据相同,则表示城府添加数据

    {return true;

    }else//如果上次 url+数据 和本次url加数据不同,则不是重复提交

    {

    httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);return false;

    }

    }

    }

    }

    方案三:利用Spring AOP和redis的锁来实现防止表单重复提交

    主要是利用了redis的分布式锁机制

    1、注解:

    importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/*** 防止重复提交注解

    *@authorzzp 2018.03.11

    *@version1.0*/@Retention(RetentionPolicy.RUNTIME)//在运行时可以获取

    @Target(value = {ElementType.METHOD, ElementType.TYPE})  //作用到类,方法,接口上等

    public @interfacePreventRepetitionAnnotation {

    }

    2、AOP代码

    importjava.lang.reflect.Method;importjava.util.UUID;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpSession;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.reflect.MethodSignature;importorg.com.rlid.utils.json.JsonBuilder;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.EnableAspectJAutoProxy;importorg.springframework.stereotype.Component;importdemo.zzp.app.aop.annotation.OperaterAnnotation;importdemo.zzp.app.redis.JedisUtils;/*** 防止重复提交操作AOP类

    *@authorzzp 2018.03.10

    *@version1.0*/@Aspect

    @Component

    @EnableAspectJAutoProxy(proxyTargetClass=true)public classPreventRepetitionAspect {

    @AutowiredprivateJedisUtils jedisUtils;private static final String PARAM_TOKEN = "token";private static final String PARAM_TOKEN_FLAG =  "tokenFlag";/*** around

    *@throwsThrowable*/@Around(value=  "@annotation(demo.zzp.app.aop.annotation.PreventRepetitionAnnotation)")public Object excute(ProceedingJoinPoint  joinPoint) throwsThrowable{try{

    Object result= null;

    Object[] args=joinPoint.getArgs();for(int i = 0;i < args.length;i++){if(args[i] != null && args[i]  instanceofHttpServletRequest){

    HttpServletRequest request=  (HttpServletRequest) args[i];//被调用的方法需要加上HttpServletRequest request这个参数

    HttpSession session =request.getSession();if(request.getMethod().equalsIgnoreCase("get")){//方法为get

    result =generate(joinPoint, request, session,  PARAM_TOKEN_FLAG);

    }else{//方法为post

    result =validation(joinPoint, request, session,  PARAM_TOKEN_FLAG);

    }

    }

    }returnresult;

    }catch(Exception e) {

    e.printStackTrace();return JsonBuilder.toJson(false, "操作失败!", "执行防止重复提交功能AOP失败,原因:" +e.getMessage());

    }

    }public Object generate(ProceedingJoinPoint  joinPoint, HttpServletRequest request, HttpSession  session,String tokenFlag) throwsThrowable {

    String uuid=UUID.randomUUID().toString();

    request.setAttribute(PARAM_TOKEN, uuid);returnjoinPoint.proceed();

    }public Object validation(ProceedingJoinPoint  joinPoint, HttpServletRequest request, HttpSession  session,String tokenFlag) throwsThrowable {

    String requestFlag=request.getParameter(PARAM_TOKEN);//redis加锁

    boolean lock =  jedisUtils.tryGetDistributedLock(tokenFlag +  requestFlag, requestFlag, 60000);if(lock){//加锁成功//执行方法

    Object funcResult =joinPoint.proceed();//方法执行完之后进行解锁

    jedisUtils.releaseDistributedLock(tokenFlag +requestFlag, requestFlag);returnfuncResult;

    }else{//锁已存在

    return JsonBuilder.toJson(false, "不能重复提交!",  null);

    }

    }

    }

    3、Controller代码

    @RequestMapping(value = "/index",method =RequestMethod.GET)

    @PreventRepetitionAnnotationpublic String toIndex(HttpServletRequest  request,Mapmap){return "form";

    }

    @RequestMapping(value= "/add",method =RequestMethod.POST)

    @ResponseBody

    @PreventRepetitionAnnotationpublicString add(HttpServletRequest request){try{

    Thread.sleep(5000);

    }catch(InterruptedException e) {

    e.printStackTrace();

    }return JsonBuilder.toJson(true, "保存成功!",null);

    }

    第一次点击提交表单,判断到当前的token还没有上锁,即给该token上锁。如果连续点击提交,则提示不能重复提交,当上锁的那次操作执行完,redis释放了锁之后才能继续提交。

    更多相关内容
  • 几种防止表单重复提交的方法 禁掉提交按钮。表单提交后使用Javascript使提交按钮disable。这种方法防止心急的用户多次点击按钮。但有个问题,如果客户端把Javascript给禁止掉,这种方法就无效了。 我之前的文章曾说...
  • 主要介绍了浅谈利用Session防止表单重复提交,简单介绍表单重复提交的情况,分析,以及解决方法代码示例,具有一定借鉴价值,需要的朋友可以了解下。
  • 第一种:用flag标识,下面的代码设置checkSubmitFlg标志;第二种:在onsubmit事件中设置,在第一次提交后使提交按钮失效,感兴趣的朋友可以了解下
  • 主要介绍了PHP+Session防止表单重复提交的解决方法,需要的朋友可以参考下
  • springboot2.1+redis+拦截器 防止表单重复提交详细完整介绍,所用到的文件都上传了,下载即可使用。自己花了半天整理,并且测试通过,使用在实际项目中的,希望对每一个下载的朋友有帮助。
  • 防止表单重复提交有很多种方法,其不外乎,客户端脚本防止刷新,服务端token验证等等,thinkphp内置了表单token验证,可以方便的防止表单重复提交
  • 我的解决办法如下(只针对客户端): 用户点击提交按钮后给按钮添加disabled属性 代码如下:$(“input:submit”).each(function() { var srcclick = $(this).attr(“onclick”); if(typeof(srcclick)==”function”){...
  • 防止表单重复提交

    2014-08-14 17:46:07
    这是一个关于防止表单重复提交的练习,大神勿喷!
  • 自定义封装注解类,(生成token存放到redis中)通过注解的方式解决API接口幂等设计防止表单重复提交
  • 本文较为详细的汇总了PHP防止表单重复提交的几种常用方法,在PHP程序开发中有着很高的实用价值。具体方法如下: 1. 使用JS让按钮在点击一次后禁用(disable)。采用这种方法可以防止多次点击的发生,实现方式较简单...
  • php 防止表单重复提交 由于网络原因,经常出来点了提交按钮,网页没有反应,而进行再次点击。这样就导致后台收到两次提交,从而进行两次处理,本文章向大家介绍php 防止表单重复提交的几种方法 1、前端解决 方法一:...
  • 主要介绍了php通过记录IP来防止表单重复提交的方法,对比分析了通过记录IP防止重复提交与通过数据库防止重复提交的方法,供大家灵活选择与使用,是非常实用的技巧,需要的朋友可以参考下
  • 防止表单重复提交的方法(简单的token方式),内附实现代码及实现思路。
  • 在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。一、表单重复提交...

    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

    一、表单重复提交的常见应用场景

    有如下的form.jsp页面

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1

    2

    3

    4

    5

    Form表单

    6

    7

    8

    9

    10 用户名:

    11

    12

    13

    14

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    form表单提交到DoFormServlet进行处理

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 package xdp.gacl.session;

    2

    3 import java.io.IOException;

    4 import javax.servlet.ServletException;

    5 import javax.servlet.http.HttpServlet;

    6 import javax.servlet.http.HttpServletRequest;

    7 import javax.servlet.http.HttpServletResponse;

    8

    9 public class DoFormServlet extends HttpServlet {

    10

    11 public void doGet(HttpServletRequest request, HttpServletResponse response)

    12 throws ServletException, IOException {

    13 //客户端是以UTF-8编码传输数据到服务器端的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码

    14 request.setCharacterEncoding("UTF-8");

    15 String userName = request.getParameter("username");

    16 try {

    17 //让当前的线程睡眠3秒钟,模拟网络延迟而导致表单重复提交的现象

    18 Thread.sleep(3*1000);

    19 } catch (InterruptedException e) {

    20 e.printStackTrace();

    21 }

    22 System.out.println("向数据库中插入数据:"+userName);

    23 }

    24

    25 public void doPost(HttpServletRequest request, HttpServletResponse response)

    26 throws ServletException, IOException {

    27 doGet(request, response);

    28 }

    29

    30 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    如果没有进行form表单重复提交处理,那么在网络延迟的情况下下面的操作将会导致form表单重复提交多次

    1.1、场景一:在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交

    演示动画如下所示:

    d05f07cffef47f6c187b5925a0c8a01d.png

    1.2、场景二:表单提交后用户点击【刷新】按钮导致表单重复提交

    演示动画如下所示:

    1d706802f97252c8f19e64c89b0fe83f.png

    点击浏览器的刷新按钮,就是把浏览器上次做的事情再做一次,因为这样也会导致表单重复提交。

    1.3、场景三:用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交

    演示动画如下所示:

    11eab7735fed70ae95dda80ba786e53e.png

    二、利用JavaScript防止表单重复提交

    既然存在上述所说的表单重复提交问题,那么我们就要想办法解决,比较常用的方法是采用JavaScript来防止表单重复提交,具体做法如下:

    修改form.jsp页面,添加如下的JavaScript代码来防止表单重复提交

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1

    2

    3

    4

    5

    Form表单

    6

    7 var isCommitted = false;//表单是否已经提交标识,默认为false

    8 function dosubmit(){

    9 if(isCommitted==false){

    10 isCommitted = true;//提交表单后,将表单是否已经提交标识设置为true

    11 return true;//返回true让表单正常提交

    12 }else{

    13 return false;//返回false那么表单将不提交

    14 }

    15 }

    16

    17

    18

    19

    20

    21 用户名:

    22

    23

    24

    25

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    我们看看使用了JavaScript来防止表单提交重复是否可以成功,运行效果如下:

    922635d2ed4156d95148fd28a7c310ee.png

    可以看到,针对"在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交"这个应用场景,使用JavaScript是可以解决这个问题的,解决的做法就是"用JavaScript控制Form表单只能提交一次"。

    除了用这种方式之外,经常见的另一种方式就是表单提交之后,将提交按钮设置为不可用,让用户没有机会点击第二次提交按钮,代码如下:

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 function dosubmit(){

    2 //获取表单提交按钮

    3 var btnSubmit = document.getElementById("submit");

    4 //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮

    5 btnSubmit.disabled= "disabled";

    6 //返回true让表单可以正常提交

    7 return true;

    8 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    运行效果如下:

    5f5c191074200055470eaa30dace83f4.png

    另外还有一种做法就是提交表单后,将提交按钮隐藏起来,这种做法和将提交按钮设置为不可用是差不多的,个人觉得将提交按钮隐藏影响到页面布局的美观,并且可能会让用户误以为是bug(怎么我一点击按钮,按钮就不见了呢?用户可能会有这样的疑问),我个人在开发中用得比较多的是表单提交后,将提交按钮设置为不可用,反正使用JavaScript防止表单重复提交的做法都是差不多的,目的都是让表单只能提交一次,这样就可以做到表单不重复提交了。

    使用JavaScript防止表单重复提交的做法只对上述提交到导致表单重复提交的三种场景中的【场景一】有效,而对于【场景二】和【场景三】是没有用,依然无法解决表单重复提交问题。

    三、利用Session防止表单重复提交

    对于【场景二】和【场景三】导致表单重复提交的问题,既然客户端无法解决,那么就在服务器端解决,在服务器端解决就需要用到session了。

    具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

    在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

    存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。

    当前用户的Session中不存在Token(令牌)。

    用户提交的表单数据中没有Token(令牌)。

    看具体的范例:

    1.创建FormServlet,用于生成Token(令牌)和跳转到form.jsp页面

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 package xdp.gacl.session;

    2

    3 import java.io.IOException;

    4 import javax.servlet.ServletException;

    5 import javax.servlet.http.HttpServlet;

    6 import javax.servlet.http.HttpServletRequest;

    7 import javax.servlet.http.HttpServletResponse;

    8

    9 public class FormServlet extends HttpServlet {

    10 private static final long serialVersionUID = -884689940866074733L;

    11

    12 public void doGet(HttpServletRequest request, HttpServletResponse response)

    13 throws ServletException, IOException {

    14

    15 String token = TokenProccessor.getInstance().makeToken();//创建令牌

    16 System.out.println("在FormServlet中生成的token:"+token);

    17 request.getSession().setAttribute("token", token); //在服务器使用session保存token(令牌)

    18 request.getRequestDispatcher("/form.jsp").forward(request, response);//跳转到form.jsp页面

    19 }

    20

    21 public void doPost(HttpServletRequest request, HttpServletResponse response)

    22 throws ServletException, IOException {

    23 doGet(request, response);

    24 }

    25

    26 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    2.在form.jsp中使用隐藏域来存储Token(令牌)

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1

    2

    3

    4

    5

    form表单

    6

    7

    8

    9

    10

    11

    12 ">

    13 --%>

    14

    15

    16 用户名:

    17

    18

    19

    20

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    3.DoFormServlet处理表单提交

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 package xdp.gacl.session;

    2

    3 import java.io.IOException;

    4 import javax.servlet.ServletException;

    5 import javax.servlet.http.HttpServlet;

    6 import javax.servlet.http.HttpServletRequest;

    7 import javax.servlet.http.HttpServletResponse;

    8

    9 public class DoFormServlet extends HttpServlet {

    10

    11 public void doGet(HttpServletRequest request, HttpServletResponse response)

    12 throws ServletException, IOException {

    13

    14 boolean b = isRepeatSubmit(request);//判断用户是否是重复提交

    15 if(b==true){

    16 System.out.println("请不要重复提交");

    17 return;

    18 }

    19 request.getSession().removeAttribute("token");//移除session中的token

    20 System.out.println("处理用户提交请求!!");

    21 }

    22

    23 /**

    24 * 判断客户端提交上来的令牌和服务器端生成的令牌是否一致

    25 * @param request

    26 * @return

    27 * true 用户重复提交了表单

    28 * false 用户没有重复提交表单

    29 */

    30 private boolean isRepeatSubmit(HttpServletRequest request) {

    31 String client_token = request.getParameter("token");

    32 //1、如果用户提交的表单数据中没有token,则用户是重复提交了表单

    33 if(client_token==null){

    34 return true;

    35 }

    36 //取出存储在Session中的token

    37 String server_token = (String) request.getSession().getAttribute("token");

    38 //2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单

    39 if(server_token==null){

    40 return true;

    41 }

    42 //3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单

    43 if(!client_token.equals(server_token)){

    44 return true;

    45 }

    46

    47 return false;

    48 }

    49

    50 public void doPost(HttpServletRequest request, HttpServletResponse response)

    51 throws ServletException, IOException {

    52 doGet(request, response);

    53 }

    54

    55 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    生成Token的工具类TokenProccessor

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 package xdp.gacl.session;

    2

    3 import java.security.MessageDigest;

    4 import java.security.NoSuchAlgorithmException;

    5 import java.util.Random;

    6 import sun.misc.BASE64Encoder;

    7

    8 public class TokenProccessor {

    9

    10 /*

    11 *单例设计模式(保证类的对象在内存中只有一个)

    12 *1、把类的构造函数私有

    13 *2、自己创建一个类的对象

    14 *3、对外提供一个公共的方法,返回类的对象

    15 */

    16 private TokenProccessor(){}

    17

    18 private static final TokenProccessor instance = new TokenProccessor();

    19

    20 /**

    21 * 返回类的对象

    22 * @return

    23 */

    24 public static TokenProccessor getInstance(){

    25 return instance;

    26 }

    27

    28 /**

    29 * 生成Token

    30 * Token:Nv6RRuGEVvmGjB+jimI/gw==

    31 * @return

    32 */

    33 public String makeToken(){ //checkException

    34 // 7346734837483 834u938493493849384 43434384

    35 String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";

    36 //数据指纹 128位长 16个字节 md5

    37 try {

    38 MessageDigest md = MessageDigest.getInstance("md5");

    39 byte md5[] = md.digest(token.getBytes());

    40 //base64编码--任意二进制编码明文字符 adfsdfsdfsf

    41 BASE64Encoder encoder = new BASE64Encoder();

    42 return encoder.encode(md5);

    43 } catch (NoSuchAlgorithmException e) {

    44 throw new RuntimeException(e);

    45 }

    46 }

    47 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    首先访问FormServlet,在FormServlet中生成Token之后再重定向到form.jsp页面,这次是在服务器端处理表单重复提交的,运行效果如下:

    2363c354787bf77fd0349c60ff9bf81f.png

    从运行效果中可以看到,通过这种方式处理表单重复提交,可以解决上述的场景二和场景三中出现的表单重复提交问题。

    展开全文
  • 由于网速等原因造成页面卡顿,用户重复刷新提交页面,甚至会有黑客或恶意用户使用工具重复恶意提交表单来对网站进行攻击,所以说防止表单重复提交在 Web 应用中的重要性是极高的。今天就和大家分享一下如何利用...

    前言

    表单重复提交是在多用户的 Web 应用中最常见且带来麻烦最多的一个问题。有很多的应用场景都会遇到表单重复提交问题,比如由于用户误操作,多次点击表单提交按钮;由于网速等原因造成页面卡顿,用户重复刷新提交页面,甚至会有黑客或恶意用户使用工具重复恶意提交表单来对网站进行攻击,所以说防止表单重复提交在 Web 应用中的重要性是极高的。

    今天就和大家分享一下如何利用自定义注解来实现防止表单重复提交✌。

    使用自定义注解实现防止表单重复提交

    我们还是先引入 Maven 依赖👇

    <!-- Spring框架基本的核心工具 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
    </dependency>
    
    <!-- SpringWeb模块 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
    <!-- 自定义验证注解 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <!--常用工具类 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <!-- io常用工具类 -->
    <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
     </dependency>
    <!-- JSON工具类 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <!-- 阿里JSON解析器 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>
    <!-- servlet包 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
    </dependency>
    

    接下来我们实现一个注解类(注解在 Java 中与类、接口的声明类似,只是所使用的关键字有所不同,声明注解使用 @interface 关键字。在底层实现上,所有定义的注解都会自动继承 java.lang.annotation.Annotation 接口。)

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义注解防止表单重复提交
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RepeatSubmit
    {
        /**
         * 间隔时间(ms),小于此时间视为重复提交
         */
        public int interval() default 5000;
    
        /**
         * 提示消息
         */
        public String message() default "不允许重复提交,请稍后再试";
    }
    

    P.S. 关于自定义注解的解释在之前的博客中有写到,这里就不再多说了,有需要了解的小伙伴请移步至:大聪明教你学Java | Spring Boot 使用自定义注解实现操作日志的记录

    下面我们再实现一个拦截器,对提交表单的过程做一个相应的拦截校验👇

    import java.lang.reflect.Method;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    /**
     * 防止重复提交拦截器
     */
    @Component
    public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
    {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
        {
            if (handler instanceof HandlerMethod)
            {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
                if (annotation != null)
                {
                    if (this.isRepeatSubmit(request, annotation))
                    {
                        //如果本次提交被认为是重复提交,则在此处做具体的逻辑处理
                        //如:弹出警告窗口等
                        return false;
                    }
                }
                return true;
            }
            else
            {
                return true;
            }
        }
    
        /**
         * 验证是否重复提交由子类实现具体的防重复提交的规则
         *
         * @param request 请求对象
         * @param annotation 防复注解
         * @return 结果
         */
        public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception;
    }
    

    我们可以看到再拦截器中一共有两个方法,分别是 preHandle 和 isRepeatSubmit。无论我们执行什么请求,都会进入 RepeatSubmitInterceptor 拦截器,进入拦截器后先执行 preHandle 方法进行预处理,判断本次拦截的方法是否增加了 RepeatSubmit 自定义注解,如果增加了该注解才会进行具体的校验。isRepeatSubmit 方法是防止表单重复提交的规则,我们通过子类来实现 isRepeatSubmit 具体的校验规则👇

    import java.util.HashMap;
    import java.util.Map;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    
    import com.mumu.framework.interceptor.RepeatSubmitInterceptor;
    import org.springframework.stereotype.Component;
    import com.mumu.common.annotation.RepeatSubmit;
    import com.xxx.xxx.xxx.JSON; 
    
    /**
     * 判断请求url和数据是否和上一次相同
     * com.xxx.xxx.xxx.JSON; 是自定义的json工具类,具体代码贴在后面
     */
    @Component
    public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
    {
        public final String REPEAT_PARAMS = "repeatParams";
    
        public final String REPEAT_TIME = "repeatTime";
    
        public final String SESSION_REPEAT_KEY = "repeatData";
    
        @SuppressWarnings("unchecked")
        @Override
        public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception
        {
            // 本次参数及系统时间
            String nowParams = JSON.marshal(request.getParameterMap());
            Map<String, Object> nowDataMap = new HashMap<String, Object>();
            nowDataMap.put(REPEAT_PARAMS, nowParams);
            nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
    
            // 请求地址(作为存放session的key值)
            String url = request.getRequestURI();
    
            HttpSession session = request.getSession();
            Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);
            if (sessionObj != null)
            {
                Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
                if (sessionMap.containsKey(url))
                {
                    Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                    if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))
                    {
                        return true;
                    }
                }
            }
            Map<String, Object> sessionMap = new HashMap<String, Object>();
            sessionMap.put(url, nowDataMap);
            session.setAttribute(SESSION_REPEAT_KEY, sessionMap);
            return false;
        }
    
        /**
         * 判断参数是否相同
         */
        private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
        {
            String nowParams = (String) nowMap.get(REPEAT_PARAMS);
            String preParams = (String) preMap.get(REPEAT_PARAMS);
            return nowParams.equals(preParams);
        }
    
        /**
         * 判断两次间隔时间
         */
        private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval)
        {
            long time1 = (Long) nowMap.get(REPEAT_TIME);
            long time2 = (Long) preMap.get(REPEAT_TIME);
            if ((time1 - time2) < interval)
            {
                return true;
            }
            return false;
        }
    }
    

    自定义 Json 工具类👇

    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import com.fasterxml.jackson.core.JsonGenerationException;
    import com.fasterxml.jackson.core.JsonParseException;
    import com.fasterxml.jackson.databind.JsonMappingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.ObjectWriter;
    
    /**
     * JSON解析处理
     */
    public class JSON
    {
        private static final ObjectMapper objectMapper = new ObjectMapper();
        private static final ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
    
        public static void marshal(File file, Object value) throws Exception
        {
            try
            {
                objectWriter.writeValue(file, value);
            }
            catch (JsonGenerationException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static void marshal(OutputStream os, Object value) throws Exception
        {
            try
            {
                objectWriter.writeValue(os, value);
            }
            catch (JsonGenerationException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static String marshal(Object value) throws Exception
        {
            try
            {
                return objectWriter.writeValueAsString(value);
            }
            catch (JsonGenerationException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static byte[] marshalBytes(Object value) throws Exception
        {
            try
            {
                return objectWriter.writeValueAsBytes(value);
            }
            catch (JsonGenerationException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static <T> T unmarshal(File file, Class<T> valueType) throws Exception
        {
            try
            {
                return objectMapper.readValue(file, valueType);
            }
            catch (JsonParseException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static <T> T unmarshal(InputStream is, Class<T> valueType) throws Exception
        {
            try
            {
                return objectMapper.readValue(is, valueType);
            }
            catch (JsonParseException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static <T> T unmarshal(String str, Class<T> valueType) throws Exception
        {
            try
            {
                return objectMapper.readValue(str, valueType);
            }
            catch (JsonParseException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    
        public static <T> T unmarshal(byte[] bytes, Class<T> valueType) throws Exception
        {
            try
            {
                if (bytes == null)
                {
                    bytes = new byte[0];
                }
                return objectMapper.readValue(bytes, 0, bytes.length, valueType);
            }
            catch (JsonParseException e)
            {
                throw new Exception(e);
            }
            catch (JsonMappingException e)
            {
                throw new Exception(e);
            }
            catch (IOException e)
            {
                throw new Exception(e);
            }
        }
    }
    
    

    在校验的过程中主要针对三个部分做校验:请求地址、请求参数以及请求时间,只有当请求地址和请求参数一致且两次请求的时间间隔小于xx毫秒时(xx毫秒即为在自定义注解中指定的时间),才会被判定为是重复提交。这种校验方式较为繁琐(毕竟需要比对三部分内容),但是它可以尽可能的避免出现“误拦截”的情况,让系统有更高的可用性和安全性。

    本文中提到的方法只是众多方法中的一种,我个人也是通过这种方式来实现的防止表单重复提交。当然了,我们可以选择的办法有很多,比如采用 JS 禁用提交按钮、利用Session防止表单重复提交等等,最终如何选择还是要根据自己的应用和开发框架来决定,选择一个适合自己的方法才是最好的💪。

    小结

    本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

    希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

    如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

    你在被打击时,记起你的珍贵,抵抗恶意;
    你在迷茫时,坚信你的珍贵,抛开蜚语;
    爱你所爱 行你所行 听从你心 无问东西

    展开全文
  • 本文实例讲述了PHP使用token防止表单重复提交的方法。分享给大家供大家参考,具体如下: <?php /* * PHP使用token防止表单重复提交 * 此处理方法纯粹是为了给初学者参考 */ session_start(); function set_token...
  • 本篇文章主要介绍了springMVC中基于token防止表单重复提交方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 在php中如何防止表单重复提交?其实也有几种解决方法。下面小编就为大家介绍一下吧。需要的朋友可以过来参考下
  • 重复提交、重复刷新、防止后退等等都是属于系统为避免重复记录而需要解决的问题,在客户端去处理需要针对每一种的可能提出相应的解决方案,然而在服务器端看来只不过是对于数据真实性的检验问题
  • 在开发中,如果一个新增或修改的表单,在后台完成数据库操作后我们设定的不是跳转到其他页面,还是返回本页面,这时点击浏览器的后退再提交或刷新页面,会导致form表单重复提交,即这条记录会被增加或修改两次。...
  • 后台防止表单重复提交的三种方法

    千次阅读 2021-04-06 21:52:54
    方案一:利用Session防止表单重复提交 具体的做法:  1、获取用户填写用户名和密码的页面时向后台发送一次请求,这时后台会生成唯一的随机标识号,专业术语称为Token(令牌)。 2、将Token发送到客户端的Form表单中,...

    方案一:利用Session防止表单重复提交

    具体的做法:
      1、获取用户填写用户名和密码的页面时向后台发送一次请求,这时后台会生成唯一的随机标识号,专业术语称为Token(令牌)。

    2、将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端。

    3、服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

    看具体的范例:

    1.创建FormServlet,用于生成Token(令牌)和跳转到form.jsp页面

    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
      public class FormServlet extends HttpServlet {
        private static final long serialVersionUID = -884689940866074733L;
     
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                 throws ServletException, IOException {
     
            String token =  UUID.randomUUID().toString() ;//创建令牌
            System.out.println("在FormServlet中生成的token:"+token);
             request.getSession().setAttribute("token", token);  //在服务器使用session保存token(令牌)
             request.getRequestDispatcher("/form.jsp").forward(request, response);//跳转到form.jsp页面
         }
     
         public void doPost(HttpServletRequest request, HttpServletResponse response)
                 throws ServletException, IOException {
             doGet(request, response);
         }
     
    }
    

    2.在form.jsp中使用隐藏域来存储Token(令牌)

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
      <head>
      <title>form表单</title>
      </head>
      
      <body>
          <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post">
             <%--使用隐藏域存储生成的token--%>
             <%--
                 <input type="hidden" name="token" value="<%=session.getAttribute("token") %>">
             --%>
             <%--使用EL表达式取出存储在session中的token--%>
             <input type="hidden" name="token" value="${token}"/> 
             用户名:<input type="text" name="username"> 
             <input type="submit" value="提交">
         </form>
     </body>
     </html>
      3.DoFormServlet处理表单提交 
    
    import java.io.IOException;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      public class DoFormServlet extends HttpServlet {
     
         public void doGet(HttpServletRequest request, HttpServletResponse response)
                     throws ServletException, IOException {
     
                 boolean b = isRepeatSubmit(request);//判断用户是否是重复提交
                 if(b==true){
                     System.out.println("请不要重复提交");
                     return;
                 }
                 request.getSession().removeAttribute("token");//移除session中的token
                 System.out.println("处理用户提交请求!!");
             }
             
             /**
              * 判断客户端提交上来的令牌和服务器端生成的令牌是否一致
              * @param request
              * @return 
              *         true 用户重复提交了表单 
              *         false 用户没有重复提交表单
              */
             private boolean isRepeatSubmit(HttpServletRequest request) {
                 String client_token = request.getParameter("token");
                 //1、如果用户提交的表单数据中没有token,则用户是重复提交了表单
                 if(client_token==null){
                     return true;
                 }
                 //取出存储在Session中的token
                 String server_token = (String) request.getSession().getAttribute("token");
                 //2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单
                 if(server_token==null){
                    return true;
                 }
                 //3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单
                 if(!client_token.equals(server_token)){
                     return true;
                 }
                 
                 return false;
             }
     
         public void doPost(HttpServletRequest request, HttpServletResponse response)
                 throws ServletException, IOException {
             doGet(request, response);
         }
     
     }
    

    方案二:判断请求url和数据是否和上一次相同

    推荐,非常简单,页面不需要任何传入,只需要在验证的controller方法上写上自定义注解即可

    写好自定义注解

    import java.lang.annotation.ElementType;  
    import java.lang.annotation.Retention;  
    import java.lang.annotation.RetentionPolicy;  
    import java.lang.annotation.Target;  
      
    /** 
     * 一个用户 相同url 同时提交 相同数据 验证 
     * @author Administrator 
     * 
     */  
    @Target(ElementType.METHOD)  
    @Retention(RetentionPolicy.RUNTIME)  
    public @interface SameUrlData {  
          
    }  
    

    写好拦截器

    import java.lang.reflect.Method;  
    import java.util.HashMap;  
    import java.util.Map;  
      
    import javax.servlet.http.HttpServletRequest;  
    import javax.servlet.http.HttpServletResponse;  
      
    import org.springframework.web.method.HandlerMethod;  
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
      
    import com.thinkgem.jeesite.common.mapper.JsonMapper;  
      
    /** 
     * 一个用户 相同url 同时提交 相同数据 验证 
     * 主要通过 session中保存到的url 和 请求参数。如果和上次相同,则是重复提交表单 
     * @author Administrator 
     * 
     */  
    public class SameUrlDataInterceptor  extends HandlerInterceptorAdapter{  
          
          @Override  
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
                if (handler instanceof HandlerMethod) {  
                    HandlerMethod handlerMethod = (HandlerMethod) handler;  
                    Method method = handlerMethod.getMethod();  
                    SameUrlData annotation = method.getAnnotation(SameUrlData.class);  
                    if (annotation != null) {  
                        if(repeatDataValidator(request))//如果重复相同数据  
                            return false;  
                        else   
                            return true;  
                    }  
                    return true;  
                } else {  
                    return super.preHandle(request, response, handler);  
                }  
            }  
        /** 
         * 验证同一个url数据是否相同提交  ,相同返回true 
         * @param httpServletRequest 
         * @return 
         */  
        public boolean repeatDataValidator(HttpServletRequest httpServletRequest)  
        {  
            String params=JsonMapper.toJsonString(httpServletRequest.getParameterMap());  
            String url=httpServletRequest.getRequestURI();  
            Map<String,String> map=new HashMap<String,String>();  
            map.put(url, params);  
            String nowUrlParams=map.toString();//  
              
            Object preUrlParams=httpServletRequest.getSession().getAttribute("repeatData");  
            if(preUrlParams==null)//如果上一个数据为null,表示还没有访问页面  
            {  
                httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);  
                return false;  
            }  
            else//否则,已经访问过页面  
            {  
                if(preUrlParams.toString().equals(nowUrlParams))//如果上次url+数据和本次url+数据相同,则表示城府添加数据  
                {  
                      
                    return true;  
                }  
                else//如果上次 url+数据 和本次url加数据不同,则不是重复提交  
                {  
                    httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);  
                    return false;  
                }  
                  
            }  
        }  
      
    }  
    <mvc:interceptor>  
         <mvc:mapping path="/**"/>  
         <bean class="*.*.SameUrlDataInterceptor"/>  
    </mvc:interceptor> 
    

    方案三:利用Spring AOP和redis的锁来实现防止表单重复提交

    主要是利用了redis的分布式锁机制

    1、注解:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    /**
    * 防止重复提交注解
    * @author zzp 2018.03.11
    * @version 1.0
    */
    @Retention(RetentionPolicy.RUNTIME) // 在运行时可以获取
    @Target(value = {ElementType.METHOD, ElementType.TYPE})  // 作用到类,方法,接口上等
    public @interface PreventRepetitionAnnotation {
    }
    

    2、AOP代码

    import java.lang.reflect.Method;
    import java.util.UUID;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.com.rlid.utils.json.JsonBuilder;
    import  org.springframework.beans.factory.annotation.Autowired;
    import  org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.stereotype.Component;
    import demo.zzp.app.aop.annotation.OperaterAnnotation;
    import demo.zzp.app.redis.JedisUtils;
    /**
    * 防止重复提交操作AOP类
    * @author zzp 2018.03.10
    * @version 1.0
    */
    @Aspect
    @Component
    @EnableAspectJAutoProxy(proxyTargetClass=true)
    public class PreventRepetitionAspect {
         
         @Autowired
         private JedisUtils jedisUtils;
         private static final String PARAM_TOKEN = "token";
        private static final String PARAM_TOKEN_FLAG =  "tokenFlag";
        
        /**
          * around
          * @throws Throwable
          */
         @Around(value =  "@annotation(demo.zzp.app.aop.annotation.PreventRepetitionAnnotation)")
         public Object excute(ProceedingJoinPoint  joinPoint) throws Throwable{
             try {
                  Object result = null;
                  Object[] args = joinPoint.getArgs();
                  for(int i = 0;i < args.length;i++){
                      if(args[i] != null && args[i]  instanceof HttpServletRequest){
                           HttpServletRequest request =  (HttpServletRequest) args[i];//被调用的方法需要加上HttpServletRequest request这个参数
                           HttpSession session =  request.getSession();
                           if(request.getMethod().equalsIgnoreCase("get")){
                                //方法为get
                                result =  generate(joinPoint, request, session,  PARAM_TOKEN_FLAG);
                           }else{
                                //方法为post
                                result =  validation(joinPoint, request, session,  PARAM_TOKEN_FLAG);
                           }
                      }
                  }
                  
                  return result;
             } catch (Exception e) {
                  e.printStackTrace();
                  return JsonBuilder.toJson(false, "操作失败!", "执行防止重复提交功能AOP失败,原因:" +  e.getMessage());
             }
         }
         
         public Object generate(ProceedingJoinPoint  joinPoint, HttpServletRequest request, HttpSession  session,String tokenFlag) throws Throwable {
            String uuid = UUID.randomUUID().toString();
            request.setAttribute(PARAM_TOKEN, uuid);
            return joinPoint.proceed();
        }
         
         public Object validation(ProceedingJoinPoint  joinPoint, HttpServletRequest request, HttpSession  session,String tokenFlag) throws Throwable {
             String requestFlag =  request.getParameter(PARAM_TOKEN);
             //redis加锁
             boolean lock =  jedisUtils.tryGetDistributedLock(tokenFlag +  requestFlag, requestFlag, 60000);
             if(lock){
                  //加锁成功
                  //执行方法
                  Object funcResult = joinPoint.proceed();
                  //方法执行完之后进行解锁
                  jedisUtils.releaseDistributedLock(tokenFlag +  requestFlag, requestFlag);
                  return funcResult;
             }else{
                  //锁已存在
                  return JsonBuilder.toJson(false, "不能重复提交!",  null);
             }
        }
         
    }
    

    3、Controller代码

    @RequestMapping(value = "/index",method =  RequestMethod.GET)
    @PreventRepetitionAnnotation
     public String toIndex(HttpServletRequest  request,Map<String, Object> map){
             return "form";
      }
         
      @RequestMapping(value = "/add",method =  RequestMethod.POST)
      @ResponseBody
      @PreventRepetitionAnnotation
       public String add(HttpServletRequest request){
             try {
                  Thread.sleep(5000);
             } catch (InterruptedException e) {
                  e.printStackTrace();
             }
             return JsonBuilder.toJson(true, "保存成功!",null);
         }
    

    第一次点击提交表单,判断到当前的token还没有上锁,即给该token上锁。如果连续点击提交,则提示不能重复提交,当上锁的那次操作执行完,redis释放了锁之后才能继续提交。

    展开全文
  • 目录前言 前言 在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的...一般会引起表单重复提交的场景 在网络延迟的情况下让用户有时间点击多次 submit 按钮导致表单重复提交 表单提交后用户点击【刷新】按钮
  • 主要介绍了PHP实现防止表单重复提交功能,结合实例形式分析了php基于token验证防止表单重复提交的相关操作技巧,非常简单实用,需要的朋友可以参考下
  • 用户在操作表单Post数据时往往会出现表单数据重复提交的问题,尤其在Web开发中此类问题比较常见。刷新页面,后退操作以前的页面,单机多次按钮都会导致数据重复提交。此类问题是因为浏览器重复提交HTTP请求导致。...
  • 1.背景与介绍: ...因此有效防止表单重复提交有一定的必要性。 2.解决方案 2.1 通过JavaScript屏蔽提交按钮(不推荐) 通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,637
精华内容 13,854
关键字:

防止表单重复提交