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


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

    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放到只读的缓存了。。

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

  • 相关阅读:
    用AutoHotkey实现 iThoughts 思维导图节点文本一键转到Excel
    AutoHotkey+BUG小狼毫+BUG win10输入法设置的艰难摸索(未完待续)
    什么时候用接口,什么时候用抽象类?
    Java接口
    单例设计模式和多例
    MySQL大小写
    SQL LIKE 操作符
    SQL DELETE 语句
    IDEA自动补全返回值类型的快捷键
    IDEA导入jar包
  • 原文地址:https://www.cnblogs.com/ylsforever/p/8532665.html
Copyright © 2020-2023  润新知