0
点赞
收藏
分享

微信扫一扫

Springboot通过aop限制同一ip接口请求次数

大明宫 2022-03-12 阅读 58

1.如何实现

限制接口请求次数的需要在接口请求之前做操作判断,所以可以通过aop或者拦截器来实现

2.具体实现

1.引入相关jar包

expiringmap是可以设置过期时间的map并且线程安全,也可以通过定时任务定时清空普通map

 <!-- AOP依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
        <!-- Map依赖 -->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.8</version>
        </dependency>

2.新增自定义注解

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentLimiting {
    long time() default 60000; // 限制时间 单位:毫秒(当前一分钟)
     int value() default 5; // 允许请求的次数
}

3.新增自定义切面类

package com.wxw.userdemo.aop;

import com.wxw.userdemo.annotation.CurrentLimiting;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author wxw
 * @data 2022/3/12 11 :21
 * @description
 */
@Aspect
@Component
public class CurrentLimitingAspect {

    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();

    /**
     *  层切点
     */
    @Pointcut("@annotation(currentLimiting)")
    public void controllerAspect(CurrentLimiting currentLimiting) {
    }

    @Around("controllerAspect(currentLimiting)")
    public Object doAround(ProceedingJoinPoint pjp, CurrentLimiting currentLimiting) throws Throwable {
        // 获得request对象
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        // 获取Map value对象, 如果没有则返回默认值
        // //getOrDefault获取参数,获取不到则给默认值
        ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
        Integer uCount = uc.getOrDefault(request.getRemoteAddr(), 0);

        if (uCount >= currentLimiting.value()) { // 超过次数,不执行目标方法
            return "接口请求超过次数";
        } else if (uCount == 0){ // 第一次请求时,设置有效时间
            uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, currentLimiting.time(), TimeUnit.MILLISECONDS);
        } else { // 未超过次数, 记录加一
            uc.put(request.getRemoteAddr(), uCount + 1);
        }
        book.put(request.getRequestURI(), uc);

        // result的值就是被拦截方法的返回值
        Object result = pjp.proceed();

        return result;
    }


}

4.介绍实现方法

使用 ConcurrentHashMap(线程安全)key存储 接口地址, value 存储 expiringmap(可设置过期时间的map)类型的 ip集合, expiringmap key 存储 ip, value 存储 次数。

5.测试接口

@RestController
@RequestMapping("/class")
public class ClassController {

    @GetMapping("/testCl")
    @CurrentLimiting()
    public String test(){
        return "调用成功";
      }
}

经测试调用5次之后返回口请求超过次数,更换另一ip则开始重新计算5次

 如何不需要限制ip单纯限制接口次数,则只需要使用expiringmap(可设置过期时间的map)key存储 接口地址,value 存储次数。

举报

相关推荐

0 条评论