Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 5083|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2025-3-18 14:43
  • 签到天数: 805 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    73万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    731050
    发表于 2021-4-24 04:35:27 | 显示全部楼层 |阅读模式

    本文介绍如何使用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...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-5-7 05:04 , Processed in 0.064629 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表