• (转载)SpringBoot Api接口防刷


    aop+redis实现ip请求方法防刷

    引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- aop -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    

    修改配置文件

    spring:
      redis:
        host: localhost
        port: 6379
        database: 1
        password: 
        timeout: 5000ms
    

    创建一个限流的注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Limiter {
    
        /**
         * 从第一次访问接口的时间到cycle周期时间内,无法超过frequency次
         *
         * @return
         */
        int frequency() default 20;
    
        /**
         * 周期时间,单位ms:
         * 默认周期时间为一分钟
         *
         * @return
         */
        long cycle() default 60 * 1000;
    
        /**
         * 返回的错误信息
         *
         * @return
         */
        String message() default "请求过于频繁";
    
        /**
         * 到期时间,单位s:
         * 如果在cycle周期时间内超过frequency次,则默认1分钟内无法继续访问
         * @return
         */
        long expireTime() default 1 * 60;
    }
    

    创建aop

    @Aspect
    @Component
    public class LimitingAspect {
    	private static final String LIMITING_KEY = "limiting:%s:%s";
        private static final String LIMITING_BEGINTIME = "beginTime";
        private static final String LIMITING_EXFREQUENCY = "exFrequency";
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        @Pointcut("@annotation(limiter)")
        public void pointcut(Limiter limiter) {
        }
    
        @Around("pointcut(limiter)")
        public Object around(ProceedingJoinPoint pjp, Limiter limiter) throws Throwable {
            //获取请求的ip和方法
            String ipAddress = WebUtil.getIpAddress();
            String methodName = pjp.getSignature().toLongString();
    
            //获取方法的访问周期和频率
            long cycle = limiter.cycle();
            int frequency = limiter.frequency();
            long currentTime = System.currentTimeMillis();
    
            //获取redis中周期内第一次访问方法的时间和执行的次数
            Long beginTimeLong = (Long) redisTemplate.opsForHash().get(String.format(LIMITING_KEY, ipAddress, methodName), LIMITING_BEGINTIME);
            Integer exFrequencyLong = (Integer) redisTemplate.opsForHash().get(String.format(LIMITING_KEY, ipAddress, methodName), LIMITING_EXFREQUENCY);
    
            long beginTime = beginTimeLong == null ? 0L : beginTimeLong;
            int exFrequency = exFrequencyLong == null ? 0 : exFrequencyLong;
    
            //如果当前时间减去周期内第一次访问方法的时间大于周期时间,则正常访问
            //并将周期内第一次访问方法的时间和执行次数初始化
            if (currentTime - beginTime > cycle) {
                redisTemplate.opsForHash().put(String.format(LIMITING_KEY, ipAddress, methodName), LIMITING_BEGINTIME, currentTime);
                redisTemplate.opsForHash().put(String.format(LIMITING_KEY, ipAddress, methodName), LIMITING_EXFREQUENCY, 1);
                redisTemplate.expire(String.format(LIMITING_KEY, ipAddress, methodName), limiter.expireTime(), TimeUnit.SECONDS);
                return pjp.proceed();
            } else {
                //如果在周期时间内,执行次数小于频率,则正常访问
                //并将执行次数加一
                if (exFrequency < frequency) {
                    redisTemplate.opsForHash().put(String.format(LIMITING_KEY, ipAddress, methodName), LIMITING_EXFREQUENCY, exFrequency + 1);
                    redisTemplate.expire(String.format(LIMITING_KEY, ipAddress, methodName), limiter.expireTime(), TimeUnit.SECONDS);
                    return pjp.proceed();
                } else {
                    //否则抛出访问频繁异常
                    throw new FrequentRequestsException(limiter.message());
                }
            }
        }   
    }
    

    创建测试类

    @RestController
    public class TestController {
    
        //限制在周期内只能访问3次
        @Limiter(frequency = 3)
        @GetMapping("getString")
        public String getString(){
            return "hello";
        }
    }
    

    测试

    浏览器中访问前三次时都能正常获取返回值,当访问第四次时抛出异常

    demo.springboot.limiter.exception.FrequentRequestsException: 请求过于频繁
        at demo.springboot.limiter.aspect.LimitingAspect.around(LimitingAspect.java:70) ~[classes/:na]
    

    需要限制访问次数的接口上加上@Limiter注解即可

    源码链接

    https://github.com/dean4lee/springboot-demo/tree/master/limiter

    转载于:

    https://blog.csdn.net/weixin_45052750/article/details/90545470

  • 相关阅读:
    [C#.NET 拾遗补漏]:迭代器和列举器
    [C#.NET 拾遗补漏]:操作符的几个骚操作
    [C#.NET 拾遗补漏]:理解 volatile 关键字
    C#-表达式目录树
    数据源管理 | 关系型分库分表,列式库分布式计算
    Java中的经典算法之冒泡排序(Bubble Sort)
    MySQL数据库优化的八种方式(经典必看)
    mysql插入数据后返回自增ID的方法(AUTO_INCREMENT)
    MySQL 插入数据后返回自增id的方法
    查询数据库中的重复数据——MySQL数据库
  • 原文地址:https://www.cnblogs.com/hjwucc/p/13047063.html
Copyright © 2020-2023  润新知