zoukankan      html  css  js  c++  java
  • 拦截器中实现防止表单重复提交

    最近项目中又出现重复数据,除了id不同,其他的完全相同的一样的数据,导致业务关系应该唯一不唯一

    问题产生的原因:以下订单举例说明,因为各种原因(网络卡,快递点击等)重复提交2个或者以上一模一样的订单,由于是同时提交的,第一个订单执行扣款生成订单未完成时候,第二个已经进来了,导致付一笔钱购买了2次或多次商品

    单机的可以按照下面方法解决,分布式的就需要用到分布式锁进行解决

    1、定义拦截器:

    package com.drawblue.device.interceptor;
    import com.alibaba.fastjson.JSONObject;
    import com.drawblue.hootul.utils.StrUtil;
    import com.drawblue.device.config.RepeatSubmit;
    import com.drawblue.device.enums.errors.CommonError;
    import com.drawblue.device.utils.SystemConstants;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    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.Map;
    import java.util.concurrent.TimeUnit;
    
    public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        // redis 有效期, 默认2s, 单位: 秒.
        private long expireTime = 2;
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
        @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(repeatSubimitValidator(request)){
                        //请求数据相同
                        logger.warn("please don't repeat submit,url:"+ request.getServletPath());
                        JSONObject result = new JSONObject ();
                        result.put("code",1);
                        result.put("meassage","repeat");
                        response.setCharacterEncoding("UTF-8");
                        response.setContentType("application/json; charset=utf-8");
                        response.getWriter().write(result.toString());
                        response.getWriter().close();
                        return false;
                    }else {//如果不是重复相同数据
                        return true;
                    }
                }
                return true;
            } else {
                return super.preHandle(request, response, handler);
            }
        }
    
    
        /**
         * 验证同一个url数据是否相同提交,相同返回true
         * @param httpServletRequest
         * @return
         */
        public boolean repeatSubimitValidator(HttpServletRequest httpServletRequest){
            //获取请求参数map
            Map<String, String[]> parameter = httpServletRequest.getParameterMap();
            String param = null;
            if(!parameter.isEmpty()){
                param = JSONObject.toJSONString(parameter);
            }else{
                param = "param is empty";
            }
            String token = httpServletRequest.getHeader(SystemConstants.TOKEN_HEADER);
            if (StringUtil.isEmpty(token)){
                //如果没有token,直接放行
                return false;
            }
            //过滤过后的请求内容
            String url = httpServletRequest.getRequestURI();
            String redisKey = token + url;
            String repeatValue = (String) this.redisTemplate.opsForValue().get(redisKey);
            if(StrUtil.isEmpty(repeatValue)){
                //如果上一个数据为null,表示还没有访问页面
                //存放并且设置有效期,2秒
                this.redisTemplate.opsForValue().set(redisKey, param, expireTime, TimeUnit.SECONDS);
                return false;
            }else{//否则,已经访问过页面
                if(param.equals(repeatValue)){
                    //如果上次url+数据和本次url+数据相同,则表示重复添加数据
                    return true;
                }else{//如果上次 url+数据 和本次url加数据不同,则不是重复提交
                    this.redisTemplate.opsForValue().set(redisKey, param, expireTime, TimeUnit.SECONDS);
                    return false;
                }
            }
        }
    }
    
    
    package com.topband.device.interceptor;
    import com.alibaba.fastjson.JSONObject;
    import com.topband.cloud.common.https.ResponseObj;
    import com.topband.cloud.common.utils.StringUtil;
    import com.topband.device.config.RepeatSubmit;
    import com.topband.device.enums.errors.CommonError;
    import com.topband.device.utils.SystemConstants;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    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.Map;
    import java.util.concurrent.TimeUnit;
    
    public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        // redis 有效期, 默认2s, 单位: 秒.
        private long expireTime = 2;
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
        @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(repeatSubimitValidator(request)){
                        //请求数据相同
                        logger.warn("please don't repeat submit,url:"+ request.getServletPath());
                        ResponseObj result = new ResponseObj();
                        result.setStatus(CommonError.STATUS_500302.getStatus());
                        result.setMessage(CommonError.STATUS_500302.getMessage());
                        response.setCharacterEncoding("UTF-8");
                        response.setContentType("application/json; charset=utf-8");
                        response.getWriter().write(result.toString());
                        response.getWriter().close();
                        return false;
                    }else {//如果不是重复相同数据
                        return true;
                    }
                }
                return true;
            } else {
                return super.preHandle(request, response, handler);
            }
        }
    
    
        /**
         * 验证同一个url数据是否相同提交,相同返回true
         * @param httpServletRequest
         * @return
         */
        public boolean repeatSubimitValidator(HttpServletRequest httpServletRequest){
            //获取请求参数map
            Map<String, String[]> parameter = httpServletRequest.getParameterMap();
            String param = null;
            if(!parameter.isEmpty()){
                param = JSONObject.toJSONString(parameter);
            }else{
                param = "param is empty";
            }
            String token = httpServletRequest.getHeader(SystemConstants.TOKEN_HEADER);
            if (StringUtil.isEmpty(token)){
                //如果没有token,直接放行
                return false;
            }
            //过滤过后的请求内容
            String url = httpServletRequest.getRequestURI();
            String redisKey = token + url;
            String repeatValue = (String) this.redisTemplate.opsForValue().get(redisKey);
            if(StringUtil.isEmpty(repeatValue)){
                //如果上一个数据为null,表示还没有访问页面
                //存放并且设置有效期,2秒
                this.redisTemplate.opsForValue().set(redisKey, param, expireTime, TimeUnit.SECONDS);
                return false;
            }else{//否则,已经访问过页面
                if(param.equals(repeatValue)){
                    //如果上次url+数据和本次url+数据相同,则表示重复添加数据
                    return true;
                }else{//如果上次 url+数据 和本次url加数据不同,则不是重复提交
                    this.redisTemplate.opsForValue().set(redisKey, param, expireTime, TimeUnit.SECONDS);
                    return false;
                }
            }
        }
    }

    2、写个标志性注解

    @Inherited
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RepeatSubmit {
    }
    3、添加到拦截器中
    registry.addInterceptor(repeatSubmitInterceptor()).addPathPatterns("/**");

    @Bean
    public RepeatSubmitInterceptor repeatSubmitInterceptor(){
    return new RepeatSubmitInterceptor();
    }
    4、在使用的方法上添加注解即可
  • 相关阅读:
    get通配符
    常用正则表达式(合)
    2.A star
    1.序
    机器人运动规划04《规划算法》
    机器人运动规划03什么是运动规划
    6.2 性能优化
    6.1 内存机制及使用优化
    5.9 热修复技术
    5.8 反射机制
  • 原文地址:https://www.cnblogs.com/xiaoyao-001/p/13404658.html
Copyright © 2011-2022 走看看