src/main/java/com/ruoyi/common/constant/Constants.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excels.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/ruoyi/framework/aspectj/lang/enums/LimitType.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/ruoyi/framework/config/RedisConfig.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/main/java/com/ruoyi/common/constant/Constants.java
@@ -75,6 +75,11 @@ public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; /** * éæµ redis key */ public static final String RATE_LIMIT_KEY = "rate_limit:"; /** * éªè¯ç æææï¼åéï¼ */ public static final Integer CAPTCHA_EXPIRATION = 2; src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
¶Ô±ÈÐÂÎļþ @@ -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.exception.ServiceException; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.framework.aspectj.lang.annotation.RateLimiter; import com.ruoyi.framework.aspectj.lang.enums.LimitType; /** * éæµå¤ç * * @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.framework.aspectj.lang.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(); } } src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excels.java
@@ -14,5 +14,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface Excels { Excel[] value(); public Excel[] value(); } src/main/java/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,40 @@ package com.ruoyi.framework.aspectj.lang.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.ruoyi.common.constant.Constants; import com.ruoyi.framework.aspectj.lang.enums.LimitType; /** * éæµæ³¨è§£ * * @author ruoyi */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimiter { /** * éæµkey */ public String key() default Constants.RATE_LIMIT_KEY; /** * éæµæ¶é´,åä½ç§ */ public int time() default 60; /** * éæµæ¬¡æ° */ public int count() default 100; /** * éæµç±»å */ public LimitType limitType() default LimitType.DEFAULT; } src/main/java/com/ruoyi/framework/aspectj/lang/enums/LimitType.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.framework.aspectj.lang.enums; /** * éæµç±»å * * @author ruoyi */ public enum LimitType { /** * é»è®¤çç¥å ¨å±éæµ */ DEFAULT, /** * æ ¹æ®è¯·æ±è IPè¿è¡éæµ */ IP } src/main/java/com/ruoyi/framework/config/RedisConfig.java
@@ -6,6 +6,7 @@ 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 @@ 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;"; } }