• 避免表单的重复提交


    Spring MVC 避免表单重复提交(涉及redis缓存)

    流程思路

    sessionId:是Request.getSession().getId() 获取session里面id

    url :是 Request.getRequestURI() 获取的网址的url

    params:是Request.getParameterMap()转json字符串再转的String

    表单第一次提交数据:

    进入拦截器,从redis 缓存中获取key(sessionId+url)为空,会把sessionId+url为key,url+param为value存入redis缓存,返回为true,进入对应 Action

    表单第二次提交数据:

    进入拦截器,从redis 缓存中获取key(sessionId+url)不为空,然后拿从缓存中获取的值和上一个进行判断,如果相同则为表单重复提交,返回false;如果不相同,返回为true,进入对应 Action

    涉及环境

    Spring MVC 框架

    Tomcat (暂无限制版本)

    Redis 缓存

    一自定义注解

    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.RetentionPolicy;
    /**
     * @author 马家立
     * @version 创建时间:2018年12月27日下午3:23:57
     * @Description:TODO 自定义注解:避免表单重复提交(一个用户 相同ur 多次提交 相同数据 验证)
     */
    @Target(ElementType.METHOD) //接口、类、枚举、注解
    @Retention(RetentionPolicy.RUNTIME)//注解会在class字节码文件中存在,在运行时可以通过反射获取到
    public @interface  SameUrlData  {
        
    }

    二拦截器

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.log4j.Logger;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import net.sf.json.JSONObject;
    /**
     * @author 马家立
     * @version 创建时间:2018年12月27日下午3:27:17
     * @Description:TODO 提交时校验页面中url和提交的参数数据并与redis缓存中的url校验,一致通过,时间过期无效
     */
    public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {
        private static Logger logger = Logger.getLogger("repeatInterceptor");
        //redis 缓存的存储过期时间(单位/s)
        static int overTime = 60;
        /**
         * 重写HandlerInterceptorAdapter的方法
         */
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
            logger.info("进入重复提交表单拦截器:SameUrlDataInterceptor的preHandle");
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                SameUrlData annotation = method.getAnnotation(SameUrlData.class);//拦截器关联注解
    if (annotation != null) {
                    //用于比较url和提交的参数数据是否相同,如果重复相同数据则返回false
                    Boolean boo = repeatDataValidator(request);
                    return boo;
                }
                return true;
            } else {
                return super.preHandle(request, response, handler);
            }
        }
    
        /**
         * @Title:repeatDataValidator
         * @author:马家立
         * @date:2018年12月28日 上午10:37:35  
         * @Description:TODO 验证同一个url数据是否相同提交 ,相同返回false  
         *                     数据储存在redis缓存当中,缓存过期则无效
         * @param:@param httpServletRequest
         * @param:@return true:url或者提交的参数数据不同;false:url相同,提交的参数数据相同
         * @return:boolean
         */
        public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
            logger.info("进入SameUrlDataInterceptor的repeatDataValidator");
            try {
                // Redis工具类
                RedisUtil redis = new RedisUtil();
                String sessionId = httpServletRequest.getSession().getId();
                logger.info("获得sessionId:" + sessionId);
                //获取参数的map,然后转为json字符串
                Map<String, String[]> mapa = httpServletRequest.getParameterMap();
                JSONObject jsonObject = JSONObject.fromObject(mapa);
                logger.info("jsonObject的输出结果是:" + jsonObject);
                //将json对象转化为json字符串
                String params = jsonObject.toString();    
                String url = httpServletRequest.getRequestURI();
                logger.info("url是:" + url);
                //创建一个新的map用于存储新的url和提交的参数数据,用于和session里面的相比较
                Map<String, String> map = new HashMap<String, String>();
                map.put(url, params);
                String nowUrlParams = map.toString();
                // 获取某个key的value
                Object preUrlParams = redis.get(sessionId + url);
                logger.info("表单拦截器配置成功");
                // 如果上一个数据为null,表示还没有访问页面
                if (preUrlParams == null) {
                    // 存储某个Key和值并设置超时时间
                    redis.setex(sessionId + url, overTime, nowUrlParams);
                    return true;
                } else {// 否则,已经访问过页面
                    // 如果上次url+数据和本次url+数据相同,则表示重复添加数据
                    if (preUrlParams.toString().equals(nowUrlParams)) {
                        return false;
                    } else { //如果上次 url+数据 和本次url加数据不同,则不是重复提交
                        redis.setex(sessionId + url, overTime, nowUrlParams);
                        return true;
                    }
                }
            } catch (Exception e) {
                logger.error("进入SameUrlDataInterceptor的repeatDataValidator");
                e.printStackTrace();
                return false;
            }
        }
    
    }

    三配置 Spring MVC 的.xml

    <mvc:mapping path="/**"/> 配置的拦截路径: "/**"表示拦截所有

    Ps只有action的方法上注解才会进行拦截

      <!-- spring 3.1版本后才支持拦截方法名,需要引入一下配置 -->
        <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
        <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
        <mvc:annotation-driven />
        <mvc:interceptors>
            <!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
            <!-- 避免表单重复提交 -->
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <!-- class的地址为避免表单重复提交的拦截器地址 -->
                <bean class="net.filter.SameUrlDataInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>

    四Action或 Controller 可直接使用注解

    五验证是否配置成功

    1.单独配置表单拦截日志

    #避免表单重复提交

    log4j.logger.repeatInterceptor = info,repeatInterceptor
    log4j.appender.repeatInterceptor=org.apache.log4j.RollingFileAppender
    log4j.appender.repeatInterceptor.File=D:\ProjectLog\OASystem\repeatInterceptor.log
    log4j.appender.repeatInterceptor.MaxFileSize=200MB
    log4j.appender.repeatInterceptor.MaxBackupIndex=10
    log4j.appender.repeatInterceptor.layout=org.apache.log4j.PatternLayout
    log4j.appender.repeatInterceptor.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS}[%p] [%l][%t]%n u3010%mu3011%n%n
    log4j.additivity.repeatInterceptor=false 

    2.查看log4j的配置路径下是否生成 repeatInterceptor.log 文件

      

    jFinal 避免表单重复提交

    流程思路

    (同Spring MVC一样)

    sessionId:是Request.getSession().getId() 获取session里面的id

    url :是 Request.getRequestURI() 获取的网址的url

    params:是Request.getParameterMap()转的json字符串再转的String

    表单第一次提交数据:

    进入拦截器,从redis 缓存中获取key(sessionId+url)为空,会把sessionId+url为key,url+param为value存入redis缓存,返回为true,进入对应 Action

    表单第二次提交数据:

    进入拦截器,从redis 缓存中获取key(sessionId+url)不为空,然后拿从缓存中获取的值和上一个进行判断,如果相同则为表单重复提交,返回false;如果不相同,返回为true,进入对应 Action

    涉及环境 

    Jfinal 框架

    Tomcat (暂无限制版本) jFinal 的核心服务器

    Redis 缓存

    一拦截器

    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.log4j.Logger;
    
    import com.jfinal.aop.Interceptor;
    import com.jfinal.aop.Invocation;
    import com.jfinal.core.Controller;
    
    import net.sf.json.JSONObject;
    
    /**
     * @author 马家立
     * @version 创建时间:2018年12月27日下午5:01:37
     * @Description:TODO 提交时校验页面中url和提交的参数数据并与redis缓存中的url校验,一致通过,时间过期无效
     */
    public class RepeatSaveInterceptor implements Interceptor {
        private static Logger logger = Logger.getLogger("repeatInterceptor");
        //redis 缓存的存储过期时间(单位/s)
        static int overTime = 60;
        /**
         * 表单重复提交拦截器
         */
        @Override
        public void intercept(Invocation inv) {
            // TODO Auto-generated method stub
            logger.info("进入重复提交表单拦截器:RepeatSaveInterceptor的intercept");
            Controller controller= inv.getController();
            //验证同一个url数据是否相同提交 ,相同返回false  
            boolean token = repeatDataValidator(controller.getRequest());
            if(token){
                inv.invoke(); // 继续执行action中的方法
            }
        }
    
        /**
         * @Title:repeatDataValidator
         * @author:马家立
         * @date:2018年12月28日 上午10:37:35  
         * @Description:TODO 验证同一个url数据是否相同提交 ,相同返回false  
         * @param:@param httpServletRequest
         * @param:@return true:url或者提交的参数数据不同;false:url相同,提交的参数数据相同
         * @return:boolean
         */
        public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
            logger.info("进入RepeatSaveInterceptor的repeatDataValidator");
            try {
                RedisUtil redis = new RedisUtil();
                String sessionId = httpServletRequest.getSession().getId();
                logger.info("获得sessionId:" + sessionId);
                //获取参数的map,然后转为json字符串
                Map<String, String[]> mapa = httpServletRequest.getParameterMap();
                JSONObject jsonObject = JSONObject.fromObject(mapa);
                logger.info("jsonObject的输出结果是:" + jsonObject);
                //将json对象转化为json字符串
                String params = jsonObject.toString();
                String url = httpServletRequest.getRequestURI();
                logger.info("url是:" + url);
                //创建一个新的map用于存储新的url和提交的参数数据,用于和session里面的相比较
                Map<String, String> map = new HashMap<String, String>();
                map.put(url, params);
                String nowUrlParams = map.toString();
                Object preUrlParams = redis.get(sessionId + url);
                logger.info("表单拦截器配置成功");
                // 如果上一个数据为null,表示还没有访问页面
                if (preUrlParams == null) {
                    redis.setex(sessionId + url, overTime, nowUrlParams);
                    return true;
                } else {// 否则,已经访问过页面
                    // 如果上次url+数据和本次url+数据相同,则表示重复添加数据
                    if (preUrlParams.toString().equals(nowUrlParams)) {
                        return false;
                    } else { //如果上次 url+数据 和本次url加数据不同,则不是重复提交
                        redis.setex(sessionId + url, overTime, nowUrlParams);
                        return true;
                    }
                }
            } catch (Exception e) {
                logger.error("进入SameUrlDataInterceptor的repeatDataValidator");
                e.printStackTrace();
                return false;
            }
        }
    }

    二Action或 Controller 可直接使用注解

    需要进行重复表单验证的方法上面加上@Before(RepeatSaveInterceptor.java)注解即可,这个注解即是第一步写的自定义注解

    三验证是否配置成功

    1.单独配置表单拦截日志

    #避免表单重复提交

    log4j.logger.repeatInterceptor = info,repeatInterceptor
    log4j.appender.repeatInterceptor=org.apache.log4j.RollingFileAppender
    log4j.appender.repeatInterceptor.File=D:\ProjectLog\OASystem\repeatInterceptor.log
    log4j.appender.repeatInterceptor.MaxFileSize=200MB
    log4j.appender.repeatInterceptor.MaxBackupIndex=10
    log4j.appender.repeatInterceptor.layout=org.apache.log4j.PatternLayout
    log4j.appender.repeatInterceptor.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS}[%p] [%l][%t]%n u3010%mu3011%n%n 
    log4j.additivity.repeatInterceptor=false 

    2.查看log4j的配置路径下是否生成 repeatInterceptor.log 文件

      

    关于注解:

    所用到的注解

    java中元注解有四个: @Retention @Target @Document @Inherited;
    @Retention:注解的保留位置         
      @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
      @Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
      @Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
      
    @Target:注解的作用目标
      @Target(ElementType.TYPE)   //接口、类、枚举、注解
      @Target(ElementType.FIELD) //字段、枚举的常量
      @Target(ElementType.METHOD) //方法
      @Target(ElementType.PARAMETER) //方法参数
      @Target(ElementType.CONSTRUCTOR)  //构造函数
      @Target(ElementType.LOCAL_VARIABLE)//局部变量
      @Target(ElementType.ANNOTATION_TYPE)//注解
      @Target(ElementType.PACKAGE) ///包   
     
    @Document:说明该注解将被包含在javadoc中
     
    @Inherited:说明子类可以继承父类中的该注解
  • 相关阅读:
    jquery 父、子页面之间页面元素的获取,方法的调用
    读excle
    dataTable写入数据库(大数据写入)
    经典类和新式类的区别
    重写父类方法
    封装redis
    继承
    私有方法
    优化MyDb

  • 原文地址:https://www.cnblogs.com/mjtabu/p/13392489.html
Copyright © 2020-2023  润新知