Spring Boot应用中实现API限流与防刷的几种方式

算法架构师 2019-06-13 ⋅ 67 阅读

在现代互联网应用中,为了保护系统的稳定和数据的安全,常常需要对API接口进行限流和防刷。本文将介绍几种在Spring Boot应用中实现API限流与防刷的方式,帮助开发人员保护接口免受滥用和恶意攻击。

1. 基于注解的限流

Spring Boot提供了自定义注解的能力,可以通过自定义注解来标注需要进行限流的接口方法。通过AOP技术,在方法执行之前判断是否需要限流,并采取相应的措施。

首先,在Spring Boot项目的pom.xml文件中添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然后,创建一个自定义注解@RateLimit

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    int value() default 1;
    int seconds() default 60;
    String key() default "default";
}

接下来,在Spring Boot的配置文件中添加限流的相关配置:

rate-limit:
  enable: true
  limit: 10
  interval: 60

然后,在项目的AOP切面中实现限流逻辑:

@Aspect
@Component
public class RateLimitAspect {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(com.example.demo.annotation.RateLimit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);

        HttpServletRequest httpRequest = (HttpServletRequest) joinPoint.getArgs()[0];
        String ip = getIpAddress(httpRequest);
        String key = rateLimit.key() + "-" + ip;

        boolean allowed = isAllowed(key, rateLimit.limit(), rateLimit.interval());
        if (allowed) {
            return joinPoint.proceed();
        } else {
            return new ApiResponse(429, "Too Many Requests");
        }
    }
  
    private boolean isAllowed(String key, int limit, int interval) {
        Long count = redisTemplate.opsForValue().increment(key, 1);
        if (count == 1) {
            redisTemplate.expire(key, interval, TimeUnit.SECONDS);
        }
        return count <= limit;
    }

    private String getIpAddress(HttpServletRequest request) {
        // 省略获取IP地址的逻辑
    }
}

最后,在需要进行限流的接口方法上加上@RateLimit注解即可:

@RestController
public class ApiController {

    @RateLimit(limit = 10, seconds = 60, key = "api")
    @GetMapping("/api/hello")
    public ApiResponse sayHello(HttpServletRequest request) {
        return new ApiResponse(200, "Hello");
    }
}

2. 使用Redis实现计数器

另一种常用的限流方式是使用Redis作为计数器。每当有请求到来时,通过自增操作更新计数器的值,并设置过期时间。如果计数器达到限制值,就拒绝请求。

首先,在Spring Boot项目的pom.xml文件中添加Redis的依赖:

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

然后,配置Redis连接信息:

spring:
  redis:
    host: localhost
    port: 6379
    password:
    database: 0

接下来,通过使用RedisTemplate来进行操作Redis:

@Component
public class RateLimiter {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public boolean isAllowed(String key, int limit, int interval) {
        String redisKey = "ratelimiter:" + key;
        Long count = redisTemplate.opsForValue().increment(redisKey, 1);
        if (count == 1) {
            redisTemplate.expire(redisKey, interval, TimeUnit.SECONDS);
        }
        return count <= limit;
    }
}

最后,在需要进行限流的地方使用RateLimiter来进行限流判断:

@RestController
public class ApiController {

    @Autowired
    private RateLimiter rateLimiter;

    @GetMapping("/api/hello")
    public ApiResponse sayHello(HttpServletRequest request) {
        if (rateLimiter.isAllowed("api", 10, 60)) {
            return new ApiResponse(200, "Hello");
        } else {
            return new ApiResponse(429, "Too Many Requests");
        }
    }
}

3. 使用第三方限流组件

除了上述两种方式外,还可以使用第三方限流组件来实现API的限流与防刷。比如常用的限流组件有Guava RateLimiter、Netflix的Hystrix、Sentinel等。

选择适合项目需求的限流组件,按照组件提供的文档和示例进行配置和使用即可。

总结

本文介绍了在Spring Boot应用中实现API限流与防刷的几种方式,包括基于注解的限流、使用Redis实现计数器和使用第三方限流组件。根据项目需求和实际情况选择合适的方式,可以有效保护API接口免受滥用和恶意攻击,提高系统的稳定性和安全性。


全部评论: 0

    我有话说: