LimitAspect.java 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*
  2. * Copyright 2019-2020 Zheng Jie
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package me.zhengjie.aspect;
  17. import com.google.common.collect.ImmutableList;
  18. import me.zhengjie.annotation.Limit;
  19. import me.zhengjie.exception.BadRequestException;
  20. import me.zhengjie.utils.RequestHolder;
  21. import me.zhengjie.utils.StringUtils;
  22. import org.aspectj.lang.ProceedingJoinPoint;
  23. import org.aspectj.lang.annotation.Around;
  24. import org.aspectj.lang.annotation.Aspect;
  25. import org.aspectj.lang.annotation.Pointcut;
  26. import org.aspectj.lang.reflect.MethodSignature;
  27. import org.slf4j.Logger;
  28. import org.slf4j.LoggerFactory;
  29. import org.springframework.data.redis.core.RedisTemplate;
  30. import org.springframework.data.redis.core.script.DefaultRedisScript;
  31. import org.springframework.data.redis.core.script.RedisScript;
  32. import org.springframework.stereotype.Component;
  33. import javax.servlet.http.HttpServletRequest;
  34. import java.lang.reflect.Method;
  35. /**
  36. * @author /
  37. */
  38. @Aspect
  39. @Component
  40. public class LimitAspect {
  41. private final RedisTemplate<Object,Object> redisTemplate;
  42. private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class);
  43. public LimitAspect(RedisTemplate<Object,Object> redisTemplate) {
  44. this.redisTemplate = redisTemplate;
  45. }
  46. @Pointcut("@annotation(me.zhengjie.annotation.Limit)")
  47. public void pointcut() {
  48. }
  49. @Around("pointcut()")
  50. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  51. HttpServletRequest request = RequestHolder.getHttpServletRequest();
  52. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  53. Method signatureMethod = signature.getMethod();
  54. Limit limit = signatureMethod.getAnnotation(Limit.class);
  55. LimitType limitType = limit.limitType();
  56. String key = limit.key();
  57. if (StringUtils.isEmpty(key)) {
  58. if (limitType == LimitType.IP) {
  59. key = StringUtils.getIp(request);
  60. } else {
  61. key = signatureMethod.getName();
  62. }
  63. }
  64. ImmutableList<Object> keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replaceAll("/","_")));
  65. String luaScript = buildLuaScript();
  66. RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
  67. Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
  68. if (null != count && count.intValue() <= limit.count()) {
  69. logger.info("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name());
  70. return joinPoint.proceed();
  71. } else {
  72. throw new BadRequestException("访问次数受限制");
  73. }
  74. }
  75. /**
  76. * 限流脚本
  77. */
  78. private String buildLuaScript() {
  79. return "local c" +
  80. "\nc = redis.call('get',KEYS[1])" +
  81. "\nif c and tonumber(c) > tonumber(ARGV[1]) then" +
  82. "\nreturn c;" +
  83. "\nend" +
  84. "\nc = redis.call('incr',KEYS[1])" +
  85. "\nif tonumber(c) == 1 then" +
  86. "\nredis.call('expire',KEYS[1],ARGV[2])" +
  87. "\nend" +
  88. "\nreturn c;";
  89. }
  90. }