SpringBoot实现基于URL和IP的访问频率限制
作者:孤蓬&听雨
1. 引言
在现代 Web 应用中,接口被恶意刷新或暴力请求是一种常见的攻击手段。为了保护系统资源,防止服务器过载或服务不可用,需要对接口的访问频率进行限制。本文将介绍如何使用 Spring Boot 实现基于 URL 和 IP 的访问频率限制,具体步骤包括:
使用拦截器拦截请求:在每个请求到达控制器之前进行拦截。
使用 Redis 存储访问记录:利用 Redis 的高性能特性来存储每个 IP 对每个 URL 的访问次数。
检测访问频率:判断 IP 在一定时间内对特定 URL 的访问次数是否超过限制。
禁用恶意 IP:如果超过限制,则将 IP 列入黑名单,禁止其后续访问。
2. 项目依赖
首先,在 pom.xml 中添加必要的依赖,包括 Spring Boot Web、Spring Boot Starter Data Redis 和 Lombok(用于简化代码)。
<!-- Spring Boot Starter Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>3.4.1</version> </dependency> <!-- Lombok (可选,用于简化代码) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
3. 配置 Redis
在 application.properties 中配置 Redis 连接信息:
server.port=8080 # Redis 配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=0 # 可选:设置密码 # spring.redis.password=yourpassword
或在 application.yml 中配置Redis连接信息:
server: port: 8080 spring: application: name: urlInterceptorDemo data: # redis 配置 redis: # 地址 host: 127.0.0.1 # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # 密码 password: "123456" # 连接超时时间 timeout: 10s
4. 创建拦截器
创建一个拦截器 RateLimitInterceptor,用于拦截每个请求并执行访问频率限制逻辑。
package com.yyqq.urlinterceptordemo.Interceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import java.util.concurrent.TimeUnit; @Component public class RateLimitInterceptor implements HandlerInterceptor { @Autowired public RedisTemplate redisTemplate; // 访问频率限制:每个 IP 每个 URL 最多访问 100 次 / 分钟 private static final int MAX_REQUESTS = 10; private static final int TIME_WINDOW = 60; // 秒 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ip = getClientIP(request); String url = request.getRequestURI(); String key = "rate_limit:" + url + ":" + ip; long count = redisTemplate.opsForValue().increment(key, 1); if (count == 1) { // 设置键的过期时间为 TIME_WINDOW 秒 redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS); } if (count > MAX_REQUESTS) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("请求过于频繁,请稍后再试。"); return false; } return true; } private String getClientIP(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
代码说明
RedisTemplate:用于与 Redis 进行交互。
MAX_REQUESTS 和 TIME_WINDOW:定义每个 IP 在每个 URL 上的最大访问次数和时间窗口(60 秒)。
preHandle 方法:
获取客户端 IP 和请求的 URL。
构建 Redis 键,例如 rate_limit:/api/data:192.168.1.1。
使用 increment 方法对键进行递增,并设置过期时间。
如果访问次数超过 MAX_REQUESTS,则返回 403 状态码,并返回错误信息。
getClientIP 方法:获取客户端的真实 IP,处理代理和负载均衡的情况。
5. 注册拦截器
创建一个配置类 WebConfig,将拦截器注册到 Spring MVC 中。
package com.yyqq.urlinterceptordemo.config; import com.yyqq.urlinterceptordemo.Interceptor.RateLimitInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RateLimitInterceptor rateLimitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(rateLimitInterceptor) .addPathPatterns("/**") // 拦截所有路径 .excludePathPatterns("/error"); // 排除错误路径 } }
代码说明
- addInterceptors 方法:将自定义的拦截器添加到拦截器链中,并指定拦截的路径模式。
- addPathPatterns("/")**:拦截所有路径。
- excludePathPatterns(“/error”):排除错误路径,避免拦截器影响错误处理。
6. 创建控制器
创建一个简单的控制器 DemoController,包含一个示例接口用于测试。
package com.yyqq.urlinterceptordemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class DemoController { @GetMapping("/getData") public String getData() { return "这是数据接口,现在访问正常!"; } }
8. 测试
启动 Spring Boot 应用后,可以进行以下测试:
1.正常访问:
访问 http://localhost:8080/test/getData,应返回 “这是数据接口,现在访问正常!”。
多次快速刷新,访问次数达到 10 次后,应返回 “请求过于频繁,请稍后再试。”,并返回 403 状态码。
2.禁用 IP:
在达到限制后,等待一段时间(60 秒),再次访问应恢复正常。
9. 总结
通过结合使用 Spring Boot 拦截器和 Redis,本文实现了一种基于 URL 和 IP 的访问频率限制机制。这种机制能够有效地防止接口被恶意刷新和暴力请求,保护系统资源,提高应用的安全性和稳定性。在实际应用中,可以根据具体需求调整访问频率限制的参数,如最大访问次数和时间窗口。此外,还可以结合其他安全措施,如 IP 黑名单、验证码等,进一步增强系统的防护能力。
到此这篇关于SpringBoot实现基于URL和IP的访问频率限制的文章就介绍到这了,更多相关SpringBoot访问频率限制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!