SpringBoot注解限制接口访问次数

1.首先,需要在pom.xml文件中添加以下依赖,以使用Spring AOP和Ehcache缓存

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.6</version>
</dependency>

2.创建一个自定义注解@ApiLimit,用于标记需要限制访问次数的接口

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int limit() default 10;
    long interval() default 1000;
}

3.创建一个切面类RateLimitAspect,用于实现限制访问次数的逻辑。该切面类使用@Aspect和@Component注解进行标记,表示它是一个切面并且可以被Spring容器管理。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RateLimitAspect {
    private Map<String, Long> requestCounts = new HashMap<>();

    @Before("@annotation(rateLimit)")
    public void checkRateLimit(JoinPoint joinPoint, RateLimit rateLimit) throws InterruptedException {
        String methodName = joinPoint.getSignature().toShortString();
        long limit = rateLimit.limit();
        long interval = rateLimit.interval();

        synchronized (requestCounts) {
            long currentTime = System.currentTimeMillis();
            if (requestCounts.containsKey(methodName)) {
                long lastRequestTime = requestCounts.get(methodName);
                long elapsedTime = currentTime - lastRequestTime;
                if (elapsedTime < interval) {
                    if (requestCounts.getOrDefault(methodName, 0L) >= limit) {
                        TimeUnit.MILLISECONDS.sleep(interval - elapsedTime);
                        requestCounts.put(methodName, currentTime);
                        return;
                    }
                }
            }
            requestCounts.put(methodName, currentTime);
        }
    }
}

在RateLimitAspect类中,我们使用@Before注解来指定在目标方法执行之前执行切面逻辑。通过@annotation(rateLimit)指定了切点,它表示切入带有RateLimit注解的方法。

在checkRateLimit方法中,我们首先获取目标方法的名称,并从requestCounts中查找该方法的上次请求时间。如果上次请求时间存在且与当前时间间隔不超过指定的interval,则判断请求次数是否超过了限制的limit。如果超过了限制,则通过TimeUnit.MILLISECONDS.sleep(interval - elapsedTime)暂停当前线程,以保持请求速率在限制范围内。

最后,我们将当前请求的时间存储到requestCounts中,以便下一次请求时使用。

4.使用以上代码,只需要在需要限制访问次数的接口方法上添加@RateLimit注解。

@RestController
public class MyController {
    @GetMapping("/myApi")
    @RateLimit(limit = 5, interval = 1000) // 每秒最多允许5次访问
    public String myApi() {
        // 处理接口逻辑
        return "Hello World!";
    }
}

注:以上代码只是一个简单示例,仅考虑了单机环境下的请求限制。在实际应用中,可能需要考虑分布式环境下的请求限制,以及更复杂的限流策略。可以根据实际需求进行相应的扩展和优化。

可以通过使用拦截器或者过滤器实现对指定接口访问次数限制。 以下是使用拦截器实现限制访问次数的示例代码: 1. 创建自定义注解 `@AccessLimit`,用于标记需要限制访问次数接口。 ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AccessLimit { // 默认访问次数限制为5次 int limit() default 5; // 时间段,单位为秒,默认为60秒 int seconds() default 60; } ``` 2. 创建拦截器 `AccessLimitInterceptor`,用于实现限制访问次数的逻辑。 ```java @Component public class AccessLimitInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 判断是否标注了@AccessLimit注解 if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; if (!handlerMethod.hasMethodAnnotation(AccessLimit.class)) { return true; } // 获取@AccessLimit注解 AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class); // 获取接口访问限制次数和时间段 int limit = accessLimit.limit(); int seconds = accessLimit.seconds(); // 获取请求的IP地址和接口地址 String ipAddr = getIpAddress(request); String requestUrl = request.getRequestURI(); // 设置Redis中的Key String redisKey = String.format("%s_%s", ipAddr, requestUrl); // 判断Redis中是否存在Key ValueOperations<String, Object> valueOps = redisTemplate.opsForValue(); if (!redisTemplate.hasKey(redisKey)) { // 第一次访问,设置初始值 valueOps.set(redisKey, 1, seconds, TimeUnit.SECONDS); } else { // 已经访问过,进行访问次数限制判断 int count = (int) valueOps.get(redisKey); if (count >= limit) { // 超出访问次数限制,返回错误信息 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("超出访问次数限制"); return false; } else { // 访问次数加1 valueOps.increment(redisKey, 1); } } } return true; } /** * 获取请求的IP地址 */ private String getIpAddress(HttpServletRequest request) { String ipAddr = request.getHeader("x-forwarded-for"); if (StringUtils.isBlank(ipAddr) || "unknown".equalsIgnoreCase(ipAddr)) { ipAddr = request.getHeader("Proxy-Client-IP"); } if (StringUtils.isBlank(ipAddr) || "unknown".equalsIgnoreCase(ipAddr)) { ipAddr = request.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isBlank(ipAddr) || "unknown".equalsIgnoreCase(ipAddr)) { ipAddr = request.getRemoteAddr(); } return ipAddr; } } ``` 3. 在需要限制访问次数接口上添加 `@AccessLimit` 注解。 ```java @RestController public class DemoController { @GetMapping("/demo") @AccessLimit public String demo() { return "Hello World!"; } } ``` 这样,每个IP地址在60秒内最多只能访问 `/demo` 接口5次。 注意:以上代码仅供参考,实际应用中还需要考虑并发访问、线程安全等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值