|
@@ -1,6 +1,8 @@
|
|
|
package com.backendsys.aspect;
|
|
|
|
|
|
import com.backendsys.exception.CustException;
|
|
|
+import com.backendsys.modules.common.config.redis.utils.RedisUtil;
|
|
|
+import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
|
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@@ -8,10 +10,9 @@ import org.aspectj.lang.JoinPoint;
|
|
|
import org.aspectj.lang.annotation.Aspect;
|
|
|
import org.aspectj.lang.annotation.Before;
|
|
|
import org.aspectj.lang.reflect.MethodSignature;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
-import org.springframework.web.context.request.RequestContextHolder;
|
|
|
-import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
|
|
|
import java.lang.reflect.Method;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
@@ -22,68 +23,35 @@ import java.util.concurrent.TimeUnit;
|
|
|
@RequiredArgsConstructor
|
|
|
public class RateLimitingAspect {
|
|
|
|
|
|
- private final RedisTemplate redisTemplate;
|
|
|
+ @Autowired
|
|
|
+ private RedisUtil redisUtil;
|
|
|
+ @Autowired
|
|
|
+ private HttpRequestUtil httpRequestUtil;
|
|
|
|
|
|
/**
|
|
|
* 带有注解的方法之前执行
|
|
|
*/
|
|
|
@SuppressWarnings("unchecked")
|
|
|
@Before("@annotation(rateLimiting)")
|
|
|
- public void doBefore(JoinPoint point, RateLimiting rateLimiting) throws Throwable {
|
|
|
- int duration = rateLimiting.duration();
|
|
|
- int limit = rateLimiting.limit();
|
|
|
- // 将接口方法和用户IP构建Redis的key
|
|
|
- String key = getCurrentLimitingKey(rateLimiting.key() + ":", point);
|
|
|
- // 未达到限流次数,自增
|
|
|
- long value = redisTemplate.opsForValue().increment(key, 1);
|
|
|
- if (value > limit) {
|
|
|
- log.error("接口限流,key:{},count:{},currentCount:{}", key, limit, value);
|
|
|
-// throw new CustExceptionSimple("访问过于频繁,请稍后再试!");
|
|
|
- throw new CustException("访问过于频繁,请稍后再试!");
|
|
|
- }
|
|
|
- // 第一次请求设置过期时间
|
|
|
- if(value == 1){
|
|
|
- redisTemplate.expire(key, duration, TimeUnit.SECONDS);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 组装 redis 的 key
|
|
|
- */
|
|
|
- private String getCurrentLimitingKey(String prefixKey,JoinPoint point) {
|
|
|
- StringBuilder sb = new StringBuilder(prefixKey);
|
|
|
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
+ public void doBefore(RateLimiting rateLimiting) {
|
|
|
|
|
|
- HttpServletRequest request = attributes.getRequest();
|
|
|
- if (request != null) sb.append(getIpAddress(request));
|
|
|
+ String key = "ratelimit-" + rateLimiting.key() + "-" + httpRequestUtil.getIpAddr() + '-' + httpRequestUtil.getUserId();
|
|
|
+ String lockKey = "lock-" + key;
|
|
|
|
|
|
- MethodSignature signature = (MethodSignature) point.getSignature();
|
|
|
- Method method = signature.getMethod();
|
|
|
- Class<?> targetClass = method.getDeclaringClass();
|
|
|
- return sb.append("-")
|
|
|
- .append( targetClass.getName())
|
|
|
- .append(".")
|
|
|
- .append(method.getName()).toString();
|
|
|
- }
|
|
|
+ // 已锁定
|
|
|
+ Integer isLocked = redisUtil.getCacheObject(lockKey);
|
|
|
+ if (isLocked != null) throw new CustException("访问过于频繁,请稍后再试!");
|
|
|
|
|
|
- /**
|
|
|
- * 获取ip地址
|
|
|
- * @param request
|
|
|
- * @return
|
|
|
- */
|
|
|
- private String getIpAddress(HttpServletRequest request) {
|
|
|
- // 从请求头或代理头中获取真实IP地址
|
|
|
- String ipAddress = request.getHeader("X-Forwarded-For");
|
|
|
- if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
|
|
|
- ipAddress = request.getHeader("Proxy-Client-IP");
|
|
|
- }
|
|
|
- if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
|
|
|
- ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
|
|
- }
|
|
|
- if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
|
|
|
- ipAddress = request.getRemoteAddr();
|
|
|
+ // 未达到限流次数,自增
|
|
|
+ long value = redisUtil.increment(key, 1);
|
|
|
+ if (value > rateLimiting.limit()) {
|
|
|
+ System.out.println("接口限流: " + key);
|
|
|
+ redisUtil.setCacheObject(lockKey, 1, rateLimiting.lockDuration(), TimeUnit.SECONDS);
|
|
|
+ throw new CustException("访问过于频繁,请稍后再试!");
|
|
|
}
|
|
|
- return ipAddress;
|
|
|
+
|
|
|
+ // 第一次请求设置过期时间
|
|
|
+ if (value == 1) redisUtil.expire(key, rateLimiting.duration(), TimeUnit.SECONDS);
|
|
|
}
|
|
|
|
|
|
}
|