精华内容
下载资源
问答
  • SpringMVC后台token重复提交解决方案

    千次阅读 2018-03-16 14:27:12
    SpringMVC后台token重复提交解决方案 本文介绍如何使用token来防止前端重复提交的问题。 目录 1.思路 2.拦截器源码实现 3.注解源码 4.拦截器的配置 5.使用指南 6.结语 思路 1.添加拦截器,拦截...

    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) {
                    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案例。

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


    关注我的公众号


    展开全文
  • 1.jsp:添加页面 生成唯一的token:令牌 111 存放到: a、HttpSession中 b、input隐藏域中 2.Servlet:处理页面 表单token=111 从HttpSession中取: 比对: 一致:保存数据;...不一致:重复提交 ...

    1.jsp:添加页面
    生成唯一的token:令牌 111
    存放到:
    a、HttpSession中
    b、input隐藏域中

    2.Servlet:处理页面

    表单:token=111
    从HttpSession中取:

    比对:
    一致:保存数据;把HttpSession中的数据删除掉。

    不一致:重复提交

    展开全文
  • 为了解决表单重复提交问题,我们选用如下代码方式:   &lt;ww:form method= "post "&gt;  &lt;ww:token/&gt;  &lt;/ww:form&gt;  在 &lt;ation&gt; 配置中增加...

         为了解决表单的重复提交问题,我们选用如下代码方式: 

         <ww:form   method= "post ">

         <ww:token/>

         </ww:form>

         在 <ation> 配置中增加

         <result   name= "invalid.token "> /error.jsp </result>

         <interceptor-ref   name= "params "> </interceptor-ref>

         <interceptor-ref   name= "token "> </interceptor-ref>

         但是在我的程序中这种办法就是不好用,经过实验发现如下配置方式也是比较好用的:

         <ww:form   method= "post ">

         <ww:token/>

         </ww:form>

         在 <ation> 配置中增加

       <interceptor-ref name="token"/>

       <interceptor-ref name="basicStack"/>

         <result   name= "invalid.token "> /error.jsp </result>

         大家可以试试。

    展开全文
  • 解决表单重复提交解决方案——在服务端对Token进行验证 1、在idea中建立一个springboot项目 2、在templates目录下建立一个index.html文件 <!DOCTYPE html> <... xmlns:contex...

    解决表单重复提交的解决方案——在服务端对Token进行验证

    1、在idea中建立一个springboot项目

    2、在templates目录下建立一个index.html文件

    <!DOCTYPE html>
    <html lang="en" xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:context="http://www.springframework.org/schema/context">
    <head>
        <mvc:annotation-driven enable-matrix-variables="true"/>
        <context:component-scan base-package="com.example.demo"/>
        <mvc:default-servlet-handler/>
        <meta charset="utf-8" />
        <title>首页</title>
    
    </head>
    <body>
    <p>
        <a href="/h2">链接</a>   //点击链接,来到表单提交页面
    </p>
    </body>
    </html>
    

    3、在templates目录下建立index3.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script type="text/javascript" src="/b/js/jquery-3.4.1.min.js"></script>
        <script>
            function f2() {
                $.ajax({
                    url:"/get_token",    //向服务器端发送的请求地址
                    success:function (data) {
                        console.log(data);
                       $("#h").val(data)    //把返回的token放入input中,随表单提交给后端
                    }
                })
            }
        </script>
    </head>
    <body>
     <form action="/h3" id="dd" method="post">
         用户名:<input type="text" id="u" name="user_name">
         密码:<input type="text" id="p" name="psd" onblur="f2()">    //当鼠标离开密码输入框时,触发f()函数,向后端发送一个ajax请求
         <input type="submit" value="提交" >
         <input type="hidden" id="h" name="token">   //通过ajax请求,把服务端生成的token放入该input中
     </form>
    </body>
    </html>
    

    4、建立一个Controller

    package com.example.demo.controller;
    
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import java.util.UUID;
    
    @Controller
    public class Token_controller {
    
    
        @ResponseBody
        @RequestMapping("/get_token")  //利用ajax请求,给前端返回一个token
        public String get1(HttpServletRequest httpServletRequest) {
            UUID uuid = UUID.randomUUID();  //利用UUID获得一个token,返回给前端的表单中
            String str = uuid.toString();
            HttpSession session = httpServletRequest.getSession();
            session.setAttribute("token", str);
            return str;
        }
    
    
        @RequestMapping("/h2")
        public String get2() {
    
            return "index3";
        }
    
    
        @RequestMapping("/h3")
        public String get3(String token, HttpServletRequest httpServletRequest) throws InterruptedException {
    
            Thread.sleep(3000);  //让线程等待3秒,模拟网络拥挤的情况
            HttpSession session = httpServletRequest.getSession();
            String tokens = (String) session.getAttribute("token");
            if (tokens == null || tokens.equals("") || (!(tokens.equals(token)))) //判断从session中是否有token,若无则表单重复提交,若session
            {                                                             //中的token值与前端表单中传过来的值不相同,也提示表单重复提交
                System.out.println("提交失败!");
    
            } else {                                                      //表单提交成功
                System.out.println("提交成功!");
                session.removeAttribute("token");
                return "index";
            }
    
            return "index";    //返回首页
    
        }
    
    
    }
    
    

    5、运行项目

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    点击多次提交按钮,只有一个请求被接收,从根本上解决了表单重复提交的问题!

    6、总结

    1、在导入jQuery时要注意路径问题,若出现无法加载jQuery的问题,首先检查jQuery所在的静态资源路径是否被拦截,在springboot2.0以后,会默认拦截静态资源,需要添加一个WebMvcConfigurer来添加静态资源的目录。如果不是这个问题,再检查jQuery的请求路径是否正确,一般都是出现这个问题。
    2、在后端对tokens进行判断是应该用(tokens == null || tokens.equals("")),不然会报空指针异常。
    3、在服务器端解决是根本的方法,常用的其他几种方法,例如在前端进行判断有他的局限性,若用户点击后退,表单还是会进行再次提交。

    展开全文
  • token机制详解 注:文内关于使用验证码图片的代码请参考我的另一篇博客 java Web项目中导入验证码图片功能步骤详解 ...解决方案:将转发改为重定向即可 2)由于网速或服务器的性能,导致处理请求满,用户重复点...
  • 保证接口幂等性,表单重复提交 前台解决方案:提交后按钮禁用、置灰、页面出现遮罩后台解决方案: 使用token,每个token只能使用一次1.在调用接口之前生成对应的Token,存放至redis 2.在调用接口时,将生成的...
  • 提交之后 disable button 之类的期望的结果有效性, 最起码的要求,不能有表单重复提交也不能误报透明性, 对前端透明, 前端无感知性能, 当然是越快越好需要解决的问题后端怎么判断一个表单重复已提交的表单应该存储一...
  • spring MVC 后台token重复提交解决方案 思路 1.添加拦截器,拦截需要防重复提交的请求 2.通过注解@Token来添加token/移除token 3.前端页面表单添加 &lt;input name='token' type='hiden'&gt; ...
  • 1.在我们的web开发中,常常会遇到表单重复提交问题,那么我们的解决方案有两种:①重定向:response.sendrediect(); ②token的使用,即做个标记 下面写一个token的例子:(在tomcat上可以运行的。) 1.JSP页面,3...
  • 第一步:导入struts2的标签库 <%@taglib uri="/struts-tags" ...第二步:在表单中加入<s:token /> <s:form action="helloworld_other" method="post" namespace="/test"> <s:textfield name="...
  • 为了避免这种情况,总结了一下4点处理方案表单重复提交 常用解决办法。1.通过前端解决(请参考以下的前端代码)2.让提交按钮只触发一次(当用户点击提交后,再次点击的时候不允许调用后台接口,和3类似)3.提交按钮变灰4....
  • 使用token解决表单提交的问题

    千次阅读 2017-05-23 17:00:19
    // 解决表单重复提交方案1:PRG模式(POST-REDIRECT-GET) (重定向到结果页面) // https://en.wikipedia.org/wiki/Post/Redirect/Get请求支付PayServlet @WebServlet("/pay") public class PayServlet extends ...
  • 重复提交解决方案

    2007-04-24 15:10:00
    Struts的Token机制能够很好的解决表单重复提交的问题,基本原理是:服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给...
  • 就是这么一个小小的错误,浪费了我好多时间,在网上看了好多帖子,都是扯淡的帖子,要么说你没放置要么说你这个没放置到中,还有就是说你拦截器顺序不对,都胡扯吧啦,没说到点子上,我在此给出解决方案:你的Action...
  • 什么情况下重复提交呢?...2、提交成功后,点击浏览器的返回按钮,再进行保存操作3、服务器端运行速度慢,没处理完,浏览器又发来新的请求解决方案第一步,在表单请求页面设置标示符token_key,并添加一个隐藏域hidd...
  • 一、概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如: 订单接口, 不能多次创建订单 ... token机制 -- 防止页面重复提交 悲观锁 -- 获取数据的时候加锁(锁表或锁行)...
  • 转自 这篇博客写的很清楚,最终解决问题的方法就是在服务器端(session,或者其他存储memcache)产生Token方案
  • 文章目录一、概念二、常见解决方案三、本文实现四、实现思路五、项目简介六、项目实战 一、概念 幂等性, 通俗的说就是一个接口, 多次发起同...token机制 – 防止页面重复提交 悲观锁 – 获取数据的时候加锁(锁表或锁行
  • 单,如果对此重复提交未加处理,将会再次提交...Struts解决方案:Struts框架的令牌机制(TOKEN)很好的解决了这个表单重复提交的问题。 基本原理:服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当...
  • 防止表单重复提交

    2018-01-22 20:22:47
    防止表单重复提交 针对于重复提交的整体解决方案: 1.用redirect来解决重复提交的问题 2.点击一次之后,按钮失效 3.通过loading 4.自定义重复提交过滤器 5.解决struts2重复提交 可以结合s:token标签来解决...

空空如也

空空如也

1 2 3 4 5 6
收藏数 103
精华内容 41
关键字:

token表单重复提交解决方案