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入门到精通教程
查看: 395|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-5-15 19:02:02 | 显示全部楼层 |阅读模式

    看到公司有个部门提出了这个问题,补个粗略的解决方案。。。

    1.编写拦截器

    /**
     * Description: 防止重复提交
     *
     * @Author liam
     * @Create Date: 2018/3/9 9:22
     */
    public class AvoidReSubmitIntercepter extends HandlerInterceptorAdapter {
    
        private static final String SPLIT_FLAG = "_";
        private static final String AVOID_RE_SUBMIT_TOKEN_KEY = "identifier_token";
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (checkAvoidReSubmitTokenOn(handler,AvoidReSubmitBehavior.Check)) {
                //验证是否重复
                if (checkIsRepeatSubmit(request)) {
                    //重复提交
                    return false;
                }
            }
            return super.preHandle(request, response, handler);
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            if (checkAvoidReSubmitTokenOn(handler,AvoidReSubmitBehavior.Create)) {
                Random random = new Random();
                String uuid = UUID.randomUUID().toString().replace(SPLIT_FLAG, String.valueOf(random.nextInt(100000)));
                String tokenValue = String.valueOf(System.currentTimeMillis());
                String transferToken = uuid + SPLIT_FLAG + tokenValue;
                request.setAttribute(AVOID_RE_SUBMIT_TOKEN_KEY, transferToken);
                request.getSession(true).setAttribute(uuid, tokenValue);
            }
            super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
        * Description: 是否开启防重规则
        *
        * @Author liam
        * @Create Date: 2018/3/9 10:33
        */
        private boolean checkAvoidReSubmitTokenOn(Object handler,AvoidReSubmitBehavior behavior) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method invokeMethod = handlerMethod.getMethod();
            AvoidReSubmitToken avoidReSubmitToken = invokeMethod.getAnnotation(AvoidReSubmitToken.class);
            if (avoidReSubmitToken != null && avoidReSubmitToken.behavior().equals(behavior)) {
                return true;
            }
            return false;
    
        }
    
        private boolean checkIsRepeatSubmit(HttpServletRequest request) {
            String clientToken = request.getParameter(AVOID_RE_SUBMIT_TOKEN_KEY);
            if (StringUtils.isEmpty(clientToken)) {
                clientToken = request.getParameter(AVOID_RE_SUBMIT_TOKEN_KEY);
                if (StringUtils.isEmpty(clientToken)) {
                    return true;
                }
            }
            String[] clientTokensDetail = StringUtils.split(clientToken, SPLIT_FLAG);
            if (clientTokensDetail.length == 2) {
                String uuid = clientTokensDetail[0];
                String token = clientTokensDetail[1];
           //此处存在并发风险...阔以加锁处理 String serverToken
    = (String) request.getSession(true).getAttribute(uuid); if (StringUtils.isNotEmpty(serverToken) && token.equals(serverToken)) { request.getSession(true).removeAttribute(uuid); return false; } } return true; } }

    提供开启规则的注解:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface AvoidReSubmitToken {
    
        AvoidReSubmitBehavior behavior();
    
    }

    定义两种行为:

    public enum  AvoidReSubmitBehavior {
        Create,
        Check;
    }

    拦截器的配置:

    <!-- 拦截器配置 -->
        <mvc:interceptors>
            <!-- 配置Token拦截器,防止用户重复提交数据 -->
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <bean class="liam.AvoidReSubmitIntercepter"/>
            </mvc:interceptor>
        </mvc:interceptors>

    Java代码使用;

        @AvoidReSubmitToken(behavior = AvoidReSubmitBehavior.Create)
        @RequestMapping("test")
        public String testPage() {
            return "form/page";
        }
        @AvoidReSubmitToken(behavior = AvoidReSubmitBehavior.Check)
        @RequestMapping("potHandler")
        public String postHandler(){
            return "ok";
        }

    页面代码:

    <form id="" class="form-horizontal" action="${ctx}/postHandler" method="post">
            ......
            <input type="hidden" name="token" value="${identifier_token}"/>
            <!-- 注:name必须是identifier_token -->
            ......
    </form>


    其实该方案也可以验证提交数据是否有效,当然通常是把token放到只读的缓存了。。

    伪代码。。没测试呢。。。

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

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

    本版积分规则

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

    GMT+8, 2025-2-3 12:00 , Processed in 0.167827 second(s), 39 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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