Java通过aop实现限制同一ip请求接口的次数

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

2.具体实现
1.引入相关jar包

expiringmap是可以设置过期时间的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.test.aop;

import com.test.interface1.CurrentLimiting;
import jakarta.servlet.http.HttpServletRequest;
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 java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class CurrentLimitingAspect {

    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> map = 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> em = map.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
        Integer Count = em.getOrDefault(request.getRemoteAddr(), 0);

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

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

        return result;
    }
}

4.介绍实现方法

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

5.测试接口

@RestController
@RequestMapping("/test")
public class ClassController {
 
    @GetMapping("/test")
    @CurrentLimiting()
    public String test(){
        return "调用成功";
      }
}


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

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

猜你喜欢

转载自blog.csdn.net/m0_52191385/article/details/130965359
今日推荐