开发中可能会经常遇到短时间内由于用户的重复点击导致几秒之内重复的请求,可能就是在这几秒之内由于各种问题,比如 网络 ,事务的隔离性等等问题导致了数据的重复等问题,因此在日常开发中必须规 避这类的重复请求操作,今天就用拦截器处理一下这个问题 自定义注解 package test ; import java. lang. annotation. ElementType ; import java. lang. annotation. Retention ; import java. lang. annotation. RetentionPolicy ; import java. lang. annotation. Target ; @Target ( { ElementType . METHOD , ElementType . TYPE } ) @Retention ( RetentionPolicy . RUNTIME ) public @interface RepeatSubmit { /** * 默认失效时间时间(秒),小于或等于0表示不启用 */ long seconds ( ) default 5 ; } 拦截器逻辑 package test ; import com. ruoyi. common. exception. RepeatSubmitException ; import com. sun. org. apache. xpath. internal. operations. Bool ; import org. springframework. beans. factory. annotation. Autowired ; import org. springframework. core. annotation. AnnotationUtils ; import org. springframework. data. redis. core. StringRedisTemplate ; import org. springframework. stereotype. Component ; import org. springframework. util. StringUtils ; import org. springframework. web. method. HandlerMethod ; import org. springframework. web. servlet. HandlerInterceptor ; import javax. servlet. http. HttpServletRequest ; import javax. servlet. http. HttpServletResponse ; import java. util. Objects ; import java. util. concurrent. TimeUnit ; /** * 重复请求的拦截器 * * @Component:注解将当前类注入到IOC容器中 */ @Component public class RepeatSubmitInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean preHandle ( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ( handlerinstanceof HandlerMethod ) { //只拦截@RepeatSubmit注解 HandlerMethod method= ( HandlerMethod ) handler; //标注在方法上的注解 RepeatSubmit repeatSubmitByMethod= AnnotationUtils . findAnnotation ( method. getMethod ( ) , RepeatSubmit . class ) ; //标注在类上的注解 RepeatSubmit repeatSubmitByCls= AnnotationUtils . findAnnotation ( method. getMethod ( ) . getDeclaringClass ( ) , RepeatSubmit . class ) ; //组合判断条件,根据自己项目实际需求来,这里只用简单的身份标识做拦截示例 //没有限制重复提交,直接跳过 if ( Objects . isNull ( repeatSubmitByMethod) && Objects . isNull ( repeatSubmitByCls) ) return true ; //优先使用方法级注解,其次使用类级注解 RepeatSubmit repeatSubmit= Objects . nonNull ( repeatSubmitByMethod) ? repeatSubmitByMethod: repeatSubmitByCls; //验证注解配置的有效性:失效时间必须大于0 if ( repeatSubmit. seconds ( ) <= 0 ) { return true ; } //请求url String uri= request. getRequestURI ( ) ; //构建更精确的Redis Key:结合用户标识防止不同用户间误判 String userKey= getUserIdentifier ( request) ; String redisKey= userKey+ ":" + uri; //redis中存在返回false,不存在返回true Boolean ifAbsent= stringRedisTemplate. opsForValue ( ) . setIfAbsent ( redisKey, "1" , Objects . nonNull ( repeatSubmitByMethod) ? repeatSubmitByMethod. seconds ( ) : repeatSubmitByCls. seconds ( ) , TimeUnit . SECONDS ) ; //如果存在,表示已经请求过了,直接抛出异常,由全局异常进行处理返回指定信息 if ( ifAbsent!= null && ! ifAbsent) { throw new RepeatSubmitException ( ) ; } return true ; } return HandlerInterceptor . super . preHandle ( request, response, handler) ; } /** * 获取用户唯一标识 * 优先级:Token > SessionId > IP地址 */ private String getUserIdentifier ( HttpServletRequest request) { //尝试从请求头获取Token String token= request. getHeader ( "Authorization" ) ; if ( StringUtils . hasText ( token) ) { return token; } //尝试获取SessionId String sessionId= request. getRequestedSessionId ( ) ; if ( StringUtils . hasText ( sessionId) ) { return sessionId; } //使用IP地址作为兜底方案 String ip= getClientIp ( request) ; return StringUtils . hasText ( ip) ? ip: "unknown" ; } /** * 获取客户端真实IP */ private String getClientIp ( HttpServletRequest request) { String ip= request. getHeader ( "X-Forwarded-For" ) ; if ( StringUtils . hasText ( ip) && ! "unknown" . equalsIgnoreCase ( ip) ) { //多次反向代理后会有多个IP值,第一个为真实IP int index= ip. indexOf ( ',' ) ; if ( index!= - 1 ) { return ip. substring ( 0 , index) ; } return ip; } ip= request. getHeader ( "X-Real-IP" ) ; if ( StringUtils . hasText ( ip) && ! "unknown" . equalsIgnoreCase ( ip) ) { return ip; } ip= request. getHeader ( "Proxy-Client-IP" ) ; if ( StringUtils . hasText ( ip) && ! "unknown" . equalsIgnoreCase ( ip) ) { return ip; } ip= request. getHeader ( "WL-Proxy-Client-IP" ) ; if ( StringUtils . hasText ( ip) && ! "unknown" . equalsIgnoreCase ( ip) ) { return ip; } return request. getRemoteAddr ( ) ; } } 配置拦截器 package test ; import org. springframework. beans. factory. annotation. Autowired ; import org. springframework. web. servlet. config. annotation. InterceptorRegistry ; import org. springframework. web. servlet. config. annotation. WebMvcConfigurer ; public class WebConfig implements WebMvcConfigurer { @Autowired private RepeatSubmitInterceptor repeatSubmitInterceptor; @Override public void addInterceptors ( InterceptorRegistry registry) { final String [ ] commonExclude= { "/error" , "/files/**" } ; registry. addInterceptor ( repeatSubmitInterceptor) . excludePathPatterns ( commonExclude) ; } } 抛异常代码 package com. ruoyi. common. exception ; /** * 重复提交异常 * * @author ruoyi */ public class RepeatSubmitException extends RuntimeException { private static final long serialVersionUID= 1L ; /** * 错误提示 */ private String message; /** * 空构造方法 */ public RepeatSubmitException ( ) { this ( "不允许重复提交,请稍后再试" ) ; } public RepeatSubmitException ( String message) { super ( message) ; this . message= message; } @Override public String getMessage ( ) { return message; } public RepeatSubmitException setMessage ( String message) { this . message= message; return this ; } }