forked from aixan/RuoYi-Vue
		
	支持自定义注解实现接口限流
This commit is contained in:
		| @@ -0,0 +1,116 @@ | ||||
| package com.ruoyi.framework.aspectj; | ||||
|  | ||||
| import java.lang.reflect.Method; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 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.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.data.redis.core.RedisTemplate; | ||||
| import org.springframework.data.redis.core.script.RedisScript; | ||||
| import org.springframework.stereotype.Component; | ||||
| import com.ruoyi.common.annotation.RateLimiter; | ||||
| import com.ruoyi.common.enums.LimitType; | ||||
| import com.ruoyi.common.exception.ServiceException; | ||||
| import com.ruoyi.common.utils.ServletUtils; | ||||
| import com.ruoyi.common.utils.StringUtils; | ||||
| import com.ruoyi.common.utils.ip.IpUtils; | ||||
|  | ||||
| /** | ||||
|  * 限流处理 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Aspect | ||||
| @Component | ||||
| public class RateLimiterAspect | ||||
| { | ||||
|     private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); | ||||
|  | ||||
|     private RedisTemplate<Object, Object> redisTemplate; | ||||
|  | ||||
|     private RedisScript<Long> limitScript; | ||||
|  | ||||
|     @Autowired | ||||
|     public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) | ||||
|     { | ||||
|         this.redisTemplate = redisTemplate; | ||||
|     } | ||||
|  | ||||
|     @Autowired | ||||
|     public void setLimitScript(RedisScript<Long> limitScript) | ||||
|     { | ||||
|         this.limitScript = limitScript; | ||||
|     } | ||||
|  | ||||
|     // 配置织入点 | ||||
|     @Pointcut("@annotation(com.ruoyi.common.annotation.RateLimiter)") | ||||
|     public void rateLimiterPointCut() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Before("rateLimiterPointCut()") | ||||
|     public void doBefore(JoinPoint point) throws Throwable | ||||
|     { | ||||
|         RateLimiter rateLimiter = getAnnotationRateLimiter(point); | ||||
|         String key = rateLimiter.key(); | ||||
|         int time = rateLimiter.time(); | ||||
|         int count = rateLimiter.count(); | ||||
|  | ||||
|         String combineKey = getCombineKey(rateLimiter, point); | ||||
|         List<Object> keys = Collections.singletonList(combineKey); | ||||
|         try | ||||
|         { | ||||
|             Long number = redisTemplate.execute(limitScript, keys, count, time); | ||||
|             if (StringUtils.isNull(number) || number.intValue() > count) | ||||
|             { | ||||
|                 throw new ServiceException("访问过于频繁,请稍后再试"); | ||||
|             } | ||||
|             log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key); | ||||
|         } | ||||
|         catch (ServiceException e) | ||||
|         { | ||||
|             throw e; | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|             throw new RuntimeException("服务器限流异常,请稍后再试"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 是否存在注解,如果存在就获取 | ||||
|      */ | ||||
|     private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint) | ||||
|     { | ||||
|         Signature signature = joinPoint.getSignature(); | ||||
|         MethodSignature methodSignature = (MethodSignature) signature; | ||||
|         Method method = methodSignature.getMethod(); | ||||
|  | ||||
|         if (method != null) | ||||
|         { | ||||
|             return method.getAnnotation(RateLimiter.class); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) | ||||
|     { | ||||
|         StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); | ||||
|         if (rateLimiter.limitType() == LimitType.IP) | ||||
|         { | ||||
|             stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())); | ||||
|         } | ||||
|         MethodSignature signature = (MethodSignature) point.getSignature(); | ||||
|         Method method = signature.getMethod(); | ||||
|         Class<?> targetClass = method.getDeclaringClass(); | ||||
|         stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName()); | ||||
|         return stringBuffer.toString(); | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.data.redis.connection.RedisConnectionFactory; | ||||
| import org.springframework.data.redis.core.RedisTemplate; | ||||
| import org.springframework.data.redis.core.script.DefaultRedisScript; | ||||
| import org.springframework.data.redis.serializer.StringRedisSerializer; | ||||
| import com.fasterxml.jackson.annotation.JsonAutoDetect; | ||||
| import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||||
| @@ -47,4 +48,32 @@ public class RedisConfig extends CachingConfigurerSupport | ||||
|         template.afterPropertiesSet(); | ||||
|         return template; | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public DefaultRedisScript<Long> limitScript() | ||||
|     { | ||||
|         DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); | ||||
|         redisScript.setScriptText(limitScriptText()); | ||||
|         redisScript.setResultType(Long.class); | ||||
|         return redisScript; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 限流脚本 | ||||
|      */ | ||||
|     private String limitScriptText() | ||||
|     { | ||||
|         return "local key = KEYS[1]\n" + | ||||
|                 "local count = tonumber(ARGV[1])\n" + | ||||
|                 "local time = tonumber(ARGV[2])\n" + | ||||
|                 "local current = redis.call('get', key);\n" + | ||||
|                 "if current and tonumber(current) > count then\n" + | ||||
|                 "    return current;\n" + | ||||
|                 "end\n" + | ||||
|                 "current = redis.call('incr', key)\n" + | ||||
|                 "if tonumber(current) == 1 then\n" + | ||||
|                 "    redis.call('expire', key, time)\n" + | ||||
|                 "end\n" + | ||||
|                 "return current;"; | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 RuoYi
					RuoYi