package com.ruoyi.framework.interceptor;

import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.json.JSON;
import com.ruoyi.common.utils.ServletUtils; /**
* 防止重复提交拦截器
*
* @author ruoyi
*/
@Component
public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
{
@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 (this.isRepeatSubmit(request))
{
AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
ServletUtils.renderString(response, JSON.marshal(ajaxResult));
return false;
}
}
return true;
}
else
{
return super.preHandle(request, response, handler);
}
} /**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param httpServletRequest
* @return
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request) throws Exception;
}
package com.ruoyi.framework.interceptor.impl;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import com.ruoyi.common.json.JSON;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; /**
* 判断请求url和数据是否和上一次相同,
* 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
*
* @author ruoyi
*/
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
public final String REPEAT_PARAMS = "repeatParams"; public final String REPEAT_TIME = "repeatTime"; public final String SESSION_REPEAT_KEY = "repeatData"; /**
* 间隔时间,单位:秒 默认10秒
*
* 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
*/
private int intervalTime = 10; public void setIntervalTime(int intervalTime)
{
this.intervalTime = intervalTime;
} @SuppressWarnings("unchecked")
@Override
public boolean isRepeatSubmit(HttpServletRequest request) throws Exception
{
// 本次参数及系统时间
String nowParams = JSON.marshal(request.getParameterMap());
Map<String, Object> nowDataMap = new HashMap<String, Object>();
nowDataMap.put(REPEAT_PARAMS, nowParams);
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); // 请求地址(作为存放session的key值)
String url = request.getRequestURI(); HttpSession session = request.getSession();
Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);
if (sessionObj != null)
{
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
if (sessionMap.containsKey(url))
{
Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
{
return true;
}
}
}
Map<String, Object> sessionMap = new HashMap<String, Object>();
sessionMap.put(url, nowDataMap);
session.setAttribute(SESSION_REPEAT_KEY, sessionMap);
return false;
} /**
* 判断参数是否相同
*/
private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
{
String nowParams = (String) nowMap.get(REPEAT_PARAMS);
String preParams = (String) preMap.get(REPEAT_PARAMS);
return nowParams.equals(preParams);
} /**
* 判断两次间隔时间
*/
private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
{
long time1 = (Long) nowMap.get(REPEAT_TIME);
long time2 = (Long) preMap.get(REPEAT_TIME);
if ((time1 - time2) < (this.intervalTime * 1000))
{
return true;
}
return false;
}
}
package com.ruoyi.common.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 自定义注解防止表单重复提交
*
* @author ruoyi
*
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{ }
package com.ruoyi.common.core.domain;

import java.util.HashMap;
import com.ruoyi.common.utils.StringUtils; /**
* 操作消息提醒
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L; /** 状态码 */
public static final String CODE_TAG = "code"; /** 返回内容 */
public static final String MSG_TAG = "msg"; /** 数据对象 */
public static final String DATA_TAG = "data"; /**
* 状态类型
*/
public enum Type
{
/** 成功 */
SUCCESS(0),
/** 警告 */
WARN(301),
/** 错误 */
ERROR(500);
private final int value; Type(int value)
{
this.value = value;
} public int value()
{
return this.value;
}
} /**
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
*/
public AjaxResult()
{
} /**
* 初始化一个新创建的 AjaxResult 对象
*
* @param type 状态类型
* @param msg 返回内容
*/
public AjaxResult(Type type, String msg)
{
super.put(CODE_TAG, type.value);
super.put(MSG_TAG, msg);
} /**
* 初始化一个新创建的 AjaxResult 对象
*
* @param type 状态类型
* @param msg 返回内容
* @param data 数据对象
*/
public AjaxResult(Type type, String msg, Object data)
{
super.put(CODE_TAG, type.value);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data))
{
super.put(DATA_TAG, data);
}
} /**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
} /**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data)
{
return AjaxResult.success("操作成功", data);
} /**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
return AjaxResult.success(msg, null);
} /**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data)
{
return new AjaxResult(Type.SUCCESS, msg, data);
} /**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
} /**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(Type.WARN, msg, data);
} /**
* 返回错误消息
*
* @return
*/
public static AjaxResult error()
{
return AjaxResult.error("操作失败");
} /**
* 返回错误消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult error(String msg)
{
return AjaxResult.error(msg, null);
} /**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult error(String msg, Object data)
{
return new AjaxResult(Type.ERROR, msg, data);
}
}
package com.ruoyi.framework.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.ruoyi.common.config.Global;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; /**
* 通用配置
*
* @author ruoyi
*/
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
/**
* 首页地址
*/
@Value("${shiro.user.indexUrl}")
private String indexUrl; @Autowired
private RepeatSubmitInterceptor repeatSubmitInterceptor; /**
* 默认首页的设置,当输入域名是可以自动跳转到默认指定的网页
*/
@Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/").setViewName("forward:" + indexUrl);
} @Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 本地文件上传路径 */
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + Global.getProfile() + "/"); /** swagger配置 */
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
} /**
* 自定义拦截规则
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
}
}

RepeatSubmitInterceptor extends HandlerInterceptorAdapter的更多相关文章

  1. Spring MVC 中 HandlerInterceptorAdapter的使用

    一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理.后处理. Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度. s ...

  2. Spring MVC 中 HandlerInterceptorAdapter的使用--转载

    原文地址:http://blog.csdn.net/liuwenbo0920/article/details/7283757 一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可 ...

  3. springMvc(三)session、HandlerInterceptorAdapter

    仅供参考 设置session值,根据自己的需求设置值 /** * 登入验证 * * @return */ @RequestMapping(value = "/loginCheck.htm&q ...

  4. SpringMVC拦截器(包括自定以拦截器--实现HandlerInterceptorAdapter)(资源和权限管理)

    一,springmvc的配置 <!-- 访问拦截 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping ...

  5. Spring中RequestContextHolder以及HandlerInterceptorAdapter的使用

    1 . RequestContextHolder 的使用 想要使用RequestContextHolder,要在web.xml中配置RequestContextListener的监听才能使用. //全 ...

  6. Springboot 拦截器(HandlerInterceptorAdapter)中注入无效

    1,传统filter和HandlerInterceptorAdapter的区别 springboot对传统Filter进行增强,添加更多细粒度的操作,分别实现预处理.后处理(调用了Service并返回 ...

  7. Spring Boot使用HandlerInterceptorAdapter和WebMvcConfigurerAdapter实现原始的登录验证

    HandlerInterceptorAdapter的介绍:http://www.cnblogs.com/EasonJim/p/7704740.html,相当于一个Filter拦截器,但是这个颗粒度更细 ...

  8. Spring MVC中的拦截器/过滤器HandlerInterceptorAdapter的使用

    一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的 而在Spring中,基于Filter这种方式可以实现Bean预处理.后处理. 比如注入FilterRegistrationBean,然后 ...

  9. SpringBoot之HandlerInterceptorAdapter

    SpringBoot之HandlerInterceptorAdapter   在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器.这样就 ...

随机推荐

  1. mybatis xml <if>判断字符串相等

    mybatis 映射文件中,if标签判断字符串相等,两种方式: 因为mybatis映射文件,是使用的ognl表达式,所以在判断字符串sex变量是否是字符串Y的时候, <if test=" ...

  2. 四十六、SAP的Message中E和W区别

    一.如果写的是E,则报红色的信息,如图 效果如下 二.如果是写的是W,则报黄色的信息 效果如下

  3. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-bookmark

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  4. 理解依赖注入(DI - Dependency Injection)

    系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Contro ...

  5. Dynamic Route Matching Vue路由(1)

    Dynamic Route Matching 动态的 路由 匹配 Very often we will need to map routes with the given pattern to the ...

  6. POJ-3984 迷宫问题(BFS找最短路径并保存)

    问题: 定义一个二维数组:  int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, ...

  7. 前端基础之Html、CSS

    Html.css相关 Html Html结构: 标签 描述 <!DOCTYPE html> 文档声明 <html> 根元素 <head> 头部 <body 主 ...

  8. C语言拾遗——sscanf

    今天写题用到了sscanf,怕忘赶紧记录一下 去百度了一下这玩意的函数原型好像是长这样的,微软上扣下来的  int sscanf( const char *buffer, const char *fo ...

  9. 在线答题APP动工前的小总结

    7月26号,确定了我们这个暑假的学习任务——做一个答题软件.目的就是为了让我们在做项目中学习,在学习中提高做项目的能力.但是,由于基础知识都还没掌握牢固,所以从本周三确定项目以来,基本没有一点进展. ...

  10. 一、VIP课程:互联网工程专题 02-Git服务搭建与版本分支管理

    第二课:搭建企业私有Git服务.docx 课程概要: GIT远程通信协议详解 基于gogs 搭建WEB管理服务 一.GIT服务器搭建方式 上一节课我们讲过GIT是一个分布式版本管理系统,既然是分布那么 ...