springboot防重复提交实现自定义注解拦截器逻辑配置拦截器抛异常代码开发中可能会经常遇到短时间内由于用户的重复点击导致几秒之内重复的请求可能就是在这几秒之内由于各种问题比如 网络 事务的隔离性等等问题导致了数据的重复等问题因此在日常开发中必须规避这类的重复请求操作今天就用拦截器处理一下这个问题自定义注解packagetest;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;Target({ElementType.METHOD,ElementType.TYPE})Retention(RetentionPolicy.RUNTIME)publicinterfaceRepeatSubmit{/** * 默认失效时间时间(秒)小于或等于0表示不启用 */longseconds()default5;}拦截器逻辑packagetest;importcom.ruoyi.common.exception.RepeatSubmitException;importcom.sun.org.apache.xpath.internal.operations.Bool;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.core.annotation.AnnotationUtils;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.util.Objects;importjava.util.concurrent.TimeUnit;/** * 重复请求的拦截器 * * Component注解将当前类注入到IOC容器中 */ComponentpublicclassRepeatSubmitInterceptorimplementsHandlerInterceptor{AutowiredprivateStringRedisTemplatestringRedisTemplate;OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(handlerinstanceofHandlerMethod){//只拦截RepeatSubmit注解HandlerMethodmethod(HandlerMethod)handler;//标注在方法上的注解RepeatSubmitrepeatSubmitByMethodAnnotationUtils.findAnnotation(method.getMethod(),RepeatSubmit.class);//标注在类上的注解RepeatSubmitrepeatSubmitByClsAnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(),RepeatSubmit.class);//组合判断条件根据自己项目实际需求来这里只用简单的身份标识做拦截示例//没有限制重复提交直接跳过if(Objects.isNull(repeatSubmitByMethod)Objects.isNull(repeatSubmitByCls))returntrue;//优先使用方法级注解其次使用类级注解RepeatSubmitrepeatSubmitObjects.nonNull(repeatSubmitByMethod)?repeatSubmitByMethod:repeatSubmitByCls;//验证注解配置的有效性失效时间必须大于0if(repeatSubmit.seconds()0){returntrue;}//请求urlStringurirequest.getRequestURI();//构建更精确的Redis Key结合用户标识防止不同用户间误判StringuserKeygetUserIdentifier(request);StringredisKeyuserKey:uri;//redis中存在返回false不存在返回trueBooleanifAbsentstringRedisTemplate.opsForValue().setIfAbsent(redisKey,1,Objects.nonNull(repeatSubmitByMethod)?repeatSubmitByMethod.seconds():repeatSubmitByCls.seconds(),TimeUnit.SECONDS);//如果存在表示已经请求过了直接抛出异常由全局异常进行处理返回指定信息if(ifAbsent!null!ifAbsent){thrownewRepeatSubmitException();}returntrue;}returnHandlerInterceptor.super.preHandle(request,response,handler);}/** * 获取用户唯一标识 * 优先级Token SessionId IP地址 */privateStringgetUserIdentifier(HttpServletRequestrequest){//尝试从请求头获取TokenStringtokenrequest.getHeader(Authorization);if(StringUtils.hasText(token)){returntoken;}//尝试获取SessionIdStringsessionIdrequest.getRequestedSessionId();if(StringUtils.hasText(sessionId)){returnsessionId;}//使用IP地址作为兜底方案StringipgetClientIp(request);returnStringUtils.hasText(ip)?ip:unknown;}/** * 获取客户端真实IP */privateStringgetClientIp(HttpServletRequestrequest){Stringiprequest.getHeader(X-Forwarded-For);if(StringUtils.hasText(ip)!unknown.equalsIgnoreCase(ip)){//多次反向代理后会有多个IP值第一个为真实IPintindexip.indexOf(,);if(index!-1){returnip.substring(0,index);}returnip;}iprequest.getHeader(X-Real-IP);if(StringUtils.hasText(ip)!unknown.equalsIgnoreCase(ip)){returnip;}iprequest.getHeader(Proxy-Client-IP);if(StringUtils.hasText(ip)!unknown.equalsIgnoreCase(ip)){returnip;}iprequest.getHeader(WL-Proxy-Client-IP);if(StringUtils.hasText(ip)!unknown.equalsIgnoreCase(ip)){returnip;}returnrequest.getRemoteAddr();}}配置拦截器packagetest;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;publicclassWebConfigimplementsWebMvcConfigurer{AutowiredprivateRepeatSubmitInterceptorrepeatSubmitInterceptor;OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){finalString[]commonExclude{/error,/files/**};registry.addInterceptor(repeatSubmitInterceptor).excludePathPatterns(commonExclude);}}抛异常代码packagecom.ruoyi.common.exception;/** * 重复提交异常 * * author ruoyi */publicclassRepeatSubmitExceptionextendsRuntimeException{privatestaticfinallongserialVersionUID1L;/** * 错误提示 */privateStringmessage;/** * 空构造方法 */publicRepeatSubmitException(){this(不允许重复提交请稍后再试);}publicRepeatSubmitException(Stringmessage){super(message);this.messagemessage;}OverridepublicStringgetMessage(){returnmessage;}publicRepeatSubmitExceptionsetMessage(Stringmessage){this.messagemessage;returnthis;}}