引言
在实际开发中,为了保护接口不被恶意请求频繁调用,常常需要对接口访问进行频率限制。本文将介绍如何使用SpringBoot实现接口频率限制,限制每个IP在一定时间内的访问次数。
添加AOP依赖
首先,我们需要在项目中添加Spring Boot AOP的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建锁注解
创建自定义注解 @HasApiLimit,用于标识需要进行频率限制的接口。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HasApiLimit {
/**
* 时间范围(秒)
*/
int seconds();
/**
* 限制的次数
*/
int maxCount();
}
实现注解处理器
然后,我们需要创建一个切面 ApiLimit 实现限制逻辑,用于处理带有@HasApiLimit注解的方法。
@Aspect
@Component
public class ApiLimit {
/**
* 定义一个切点(通过注解)
*/
@Pointcut("@annotation(com.blog.aop.has.HasApiLimit)")
public void HasApiLimit() {
}
/**
* Aop切面方法
*
* @param joinPoint 切点
*/
@Before("HasApiLimit()")
public void before(JoinPoint joinPoint) {
//获取当前请求的方法上的注解中设置的值
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//反射获取当前被调用的方法
Method method = signature.getMethod();
//获取方法中的注解,看是否有该注解
HasApiLimit hasApiLimit = method.getDeclaredAnnotation(HasApiLimit.class);
int seconds = hasApiLimit.seconds();
int maxCount = hasApiLimit.maxCount();
//获取请求相关信息
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = getIpAddr(request);
String api = request.getRequestURI();
//拼接redis key
String key = "HasApiLimit:" + ip + ":" + api;
//判断key是否存在,如果不存在,则说明是第一次访问
if (!RedisUtils.hasKey(key)) {
RedisUtils.set(key, 1, seconds, TimeUnit.MINUTES);
} else {
//key存在,则获取当前访问次数
int count = Integer.parseInt(String.valueOf(RedisUtils.get(key)));
if (count >= maxCount) {
//超过了限制次数,返回错误信息
throw new BusinessException("使用频率过高," + seconds + "分钟后再试");
} else {
RedisUtils.increment(key, 1);
}
}
}
/**
* 获取真实IP地址
*
* @param request HttpServletRequest
*/
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
在方法上使用注解
在需要进行频率限制的接口方法上添加 @HasApiLimit 注解,并设置时间范围和限制次数。
@HasApiLimit(seconds = 3, maxCount = 1)
@PreAuthorize("hasAuthority('sys:news:save')")
@PostMapping()
public ApiResult<?> save(@RequestBody News news) {
if (newsService.save(news)) {
return success("添加成功");
}
return fail("添加失败");
}
注意事项⚠️
要获取真实的IP地址
因此需要在Nginx或Apache等Web服务器中配置反向代理
可以在Web服务器中添加如下配置:
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
总结
通过以上步骤,我们可以轻松实现对接口的频率限制,保护接口不被恶意请求频繁调用,提高系统的稳定性和安全性。