From 931ed0eb0036d3505fd0a4bd7b2f1d54c7d5f903 Mon Sep 17 00:00:00 2001
From: 疯狂的狮子li <15040126243@163.com>
Date: 星期六, 18 九月 2021 15:44:36 +0800
Subject: [PATCH] update [重大改动]重写 防重提交实现 使用分布式锁 解决并发问题 压测通过
---
/dev/null | 114 ----------------------------
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java | 27 +-----
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java | 93 +++++++++++++++++++++++
3 files changed, 98 insertions(+), 136 deletions(-)
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
new file mode 100644
index 0000000..b91b2e3
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
@@ -0,0 +1,93 @@
+package com.ruoyi.framework.aspectj;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import com.baomidou.lock.LockInfo;
+import com.baomidou.lock.LockTemplate;
+import com.ruoyi.common.annotation.RepeatSubmit;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.properties.TokenProperties;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+/**
+ * 闃叉閲嶅鎻愪氦
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@Aspect
+@Component
+public class RepeatSubmitAspect {
+
+ private final TokenProperties tokenProperties;
+ private final RepeatSubmitProperties repeatSubmitProperties;
+ private final LockTemplate lockTemplate;
+
+ // 閰嶇疆缁囧叆鐐�
+ @Pointcut("@annotation(com.ruoyi.common.annotation.RepeatSubmit)")
+ public void repeatSubmitPointCut() {
+ }
+
+ @Before("repeatSubmitPointCut()")
+ public void doBefore(JoinPoint point) throws Throwable {
+ RepeatSubmit repeatSubmit = getAnnotationRateLimiter(point);
+ // 濡傛灉娉ㄨВ涓嶄负0 鍒欎娇鐢ㄦ敞瑙f暟鍊�
+ long intervalTime = repeatSubmitProperties.getIntervalTime();
+ if (repeatSubmit.intervalTime() > 0) {
+ intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime());
+ }
+ if (intervalTime < 1000) {
+ throw new ServiceException("閲嶅鎻愪氦闂撮殧鏃堕棿涓嶈兘灏忎簬'1'绉�");
+ }
+ HttpServletRequest request = ServletUtils.getRequest();
+ String nowParams = StrUtil.join(",", point.getArgs());
+
+ // 璇锋眰鍦板潃锛堜綔涓哄瓨鏀綾ache鐨刱ey鍊硷級
+ String url = request.getRequestURI();
+
+ // 鍞竴鍊硷紙娌℃湁娑堟伅澶村垯浣跨敤璇锋眰鍦板潃锛�
+ String submitKey = request.getHeader(tokenProperties.getHeader());
+ if (StringUtils.isEmpty(submitKey)) {
+ submitKey = url;
+ }
+ submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
+ // 鍞竴鏍囪瘑锛堟寚瀹歬ey + 娑堟伅澶达級
+ String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
+ LockInfo lock = lockTemplate.lock(cacheRepeatKey, intervalTime, intervalTime / 2);
+ if (lock == null) {
+ throw new ServiceException("涓嶅厑璁搁噸澶嶆彁浜わ紝璇风◢鍚庡啀璇�!");
+ }
+ }
+
+ /**
+ * 鏄惁瀛樺湪娉ㄨВ锛屽鏋滃瓨鍦ㄥ氨鑾峰彇
+ */
+ private RepeatSubmit getAnnotationRateLimiter(JoinPoint joinPoint) {
+ Signature signature = joinPoint.getSignature();
+ MethodSignature methodSignature = (MethodSignature) signature;
+ Method method = methodSignature.getMethod();
+
+ if (method != null) {
+ return method.getAnnotation(RepeatSubmit.class);
+ }
+ return null;
+ }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
index 870af3f..ddf4c70 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
@@ -1,52 +1,35 @@
package com.ruoyi.framework.config;
-import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 閫氱敤閰嶇疆
*
- * @author ruoyi
+ * @author Lion Li
*/
@Configuration
-public class ResourcesConfig implements WebMvcConfigurer
-{
- @Autowired
- private RepeatSubmitInterceptor repeatSubmitInterceptor;
+public class ResourcesConfig implements WebMvcConfigurer {
@Override
- public void addResourceHandlers(ResourceHandlerRegistry registry)
- {
- }
-
- /**
- * 鑷畾涔夋嫤鎴鍒�
- */
- @Override
- public void addInterceptors(InterceptorRegistry registry)
- {
- registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
/**
* 璺ㄥ煙閰嶇疆
*/
@Bean
- public CorsFilter corsFilter()
- {
+ public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 璁剧疆璁块棶婧愬湴鍧�
- config.addAllowedOriginPattern("*");
+ config.addAllowedOriginPattern("*");
// 璁剧疆璁块棶婧愯姹傚ご
config.addAllowedHeader("*");
// 璁剧疆璁块棶婧愯姹傛柟娉�
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
deleted file mode 100644
index 9af0a83..0000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.ruoyi.framework.interceptor;
-
-import com.ruoyi.common.annotation.RepeatSubmit;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.ServletUtils;
-import org.springframework.stereotype.Component;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.HandlerInterceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.lang.reflect.Method;
-
-/**
- * 闃叉閲嶅鎻愪氦鎷︽埅鍣�
- *
- * 绉婚櫎缁ф壙 HandlerInterceptorAdapter 杩囨湡绫�
- * 鏀逛负瀹炵幇 HandlerInterceptor 鎺ュ彛(瀹樻柟鎺ㄨ崘鍐欐硶)
- *
- * @author Lion Li
- */
-@Component
-public abstract class RepeatSubmitInterceptor implements HandlerInterceptor {
-
- @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(annotation, request)) {
- AjaxResult ajaxResult = AjaxResult.error("涓嶅厑璁搁噸澶嶆彁浜わ紝璇风◢鍚庡啀璇�");
- ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
- return false;
- }
- }
- return true;
- } else {
- return HandlerInterceptor.super.preHandle(request, response, handler);
- }
- }
-
- /**
- * 楠岃瘉鏄惁閲嶅鎻愪氦鐢卞瓙绫诲疄鐜板叿浣撶殑闃查噸澶嶆彁浜ょ殑瑙勫垯
- */
- public abstract boolean isRepeatSubmit(RepeatSubmit annotation, HttpServletRequest request);
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
deleted file mode 100644
index 47b7fbb..0000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.ruoyi.framework.interceptor.impl;
-
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.io.IoUtil;
-import com.ruoyi.common.annotation.RepeatSubmit;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
-import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.RedisUtils;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
-import com.ruoyi.common.properties.TokenProperties;
-import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * 鍒ゆ柇璇锋眰url鍜屾暟鎹槸鍚﹀拰涓婁竴娆$浉鍚岋紝
- * 濡傛灉鍜屼笂娆$浉鍚岋紝鍒欐槸閲嶅鎻愪氦琛ㄥ崟銆�
- *
- * @author Lion Li
- */
-@Slf4j
-@RequiredArgsConstructor(onConstructor_ = @Autowired)
-@Component
-public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
- public final String REPEAT_PARAMS = "repeatParams";
-
- public final String REPEAT_TIME = "repeatTime";
-
- private final TokenProperties tokenProperties;
- private final RepeatSubmitProperties repeatSubmitProperties;
-
-
- @SuppressWarnings("unchecked")
- @Override
- public boolean isRepeatSubmit(RepeatSubmit repeatSubmit, HttpServletRequest request) {
- // 濡傛灉娉ㄨВ涓嶄负0 鍒欎娇鐢ㄦ敞瑙f暟鍊�
- long intervalTime = repeatSubmitProperties.getIntervalTime();
- if (repeatSubmit.intervalTime() > 0) {
- intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime());
- }
- String nowParams = "";
- if (request instanceof RepeatedlyRequestWrapper) {
- RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
- try {
- nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream());
- } catch (IOException e) {
- log.warn("璇诲彇娴佸嚭鐜伴棶棰橈紒");
- }
- }
-
- // body鍙傛暟涓虹┖锛岃幏鍙朠arameter鐨勬暟鎹�
- if (StringUtils.isEmpty(nowParams)) {
- nowParams = JsonUtils.toJsonString(request.getParameterMap());
- }
- Map<String, Object> nowDataMap = new HashMap<String, Object>();
- nowDataMap.put(REPEAT_PARAMS, nowParams);
- nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
-
- // 璇锋眰鍦板潃锛堜綔涓哄瓨鏀綾ache鐨刱ey鍊硷級
- String url = request.getRequestURI();
-
- // 鍞竴鍊硷紙娌℃湁娑堟伅澶村垯浣跨敤璇锋眰鍦板潃锛�
- String submitKey = request.getHeader(tokenProperties.getHeader());
- if (StringUtils.isEmpty(submitKey)) {
- submitKey = url;
- }
-
- // 鍞竴鏍囪瘑锛堟寚瀹歬ey + 娑堟伅澶达級
- String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
-
- Object sessionObj = RedisUtils.getCacheObject(cacheRepeatKey);
- 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, intervalTime)) {
- return true;
- }
- }
- }
- Map<String, Object> cacheMap = new HashMap<String, Object>();
- cacheMap.put(url, nowDataMap);
- RedisUtils.setCacheObject(cacheRepeatKey, cacheMap, Convert.toInt(intervalTime), TimeUnit.MILLISECONDS);
- 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 intervalTime) {
- long time1 = (Long) nowMap.get(REPEAT_TIME);
- long time2 = (Long) preMap.get(REPEAT_TIME);
- return (time1 - time2) < intervalTime;
- }
-}
--
Gitblit v1.9.3