IP限流又双叒叕被绕过了?这5种实战方案让恶意请求无处遁形!
IP限流被绕过了?这5种实战方案让恶意请求无处遁形!
大家好,我是服务端技术精选的老司机,今天咱们聊聊一个让后端工程师又爱又恨的话题——IP限流。
你是不是也遇到过这种崩溃场景:明明设置了接口限流,但还是被恶意请求给冲垮了?爬虫、刷单、DDOS攻击...各种"不速之客"轮番上阵,系统被搞得千疮百孔。
我曾经服务过一家在线教育平台,每天都有成千上万的爬虫在疯狂抓取课程信息,服务器资源被消耗殆尽,正常用户都无法访问。经过一番架构改造,我们实现了基于IP的多层限流体系,不仅挡住了恶意流量,还让系统性能提升了300%!
今天就把这套IP限流的实战方案全盘托出,让你的系统从此固若金汤!
一、为啥IP限流这么难搞?
1. IP获取的坑比你想象的多
很多同学以为IP限流很简单,不就是获取客户端IP然后计数嘛?Too young too simple!
在真实的生产环境中,获取真实IP就是第一道难关:
// 错误示例:直接获取RemoteAddr
String ip = request.getRemoteAddr(); // 这样拿到的可能是代理IP!
// 正确示例:考虑各种代理情况
public String getRealIp(HttpServletRequest request) {
String ip = null;
// 1. 先检查X-Forwarded-For头
ip = request.getHeader("X-Forwarded-For");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
ip = ip.split(",")[0].trim();
}
// 2. 检查X-Real-IP头(Nginx常用)
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
// 3. 最后才用RemoteAddr
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
return ip;
}
2. 攻击手段防不胜防
现在的攻击者越来越狡猾,常见的绕过手段有:
- 代理池轮换:使用大量代理IP轮流访问
- 分布式肉鸡:控制大量真实设备发起攻击
- IP伪造:通过修改HTTP头伪造来源IP
- 慢速攻击:控制请求频率,规避单纯的计数限流
我之前遇到过一个刷单团伙,他们使用了上万个住宅代理IP,每个IP每分钟只发3个请求,完美规避了我们的基础限流规则,差点把我们的优惠券薅光...
二、5种IP限流实战方案,从入门到精通
方案1:基础计数器限流 - 新手村装备
适用场景:小流量系统,简单防护
@Component
public class SimpleIpRateLimiter {
// 使用本地缓存存储IP计数
private final Cache<String, AtomicInteger> ipCountCache =
CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
// 每分钟最多100次请求
private static final int MAX_REQUESTS_PER_MINUTE = 100;
public boolean isAllowed(String ip) {
try {
AtomicInteger count = ipCountCache.get(ip, AtomicInteger::new);
return count.incrementAndGet() <= MAX_REQUESTS_PER_MINUTE;
} catch (ExecutionException e) {
return true; // 异常情况下放行
}
}
}
优点:实现简单,性能好
缺点:无法应对分布式环境,规则单一
方案2:Redis分布式限流 - 进阶装备
适用场景:分布式系统,中等流量
@Component
public class RedisIpRateLimiter {
@Autowired
private StringRedisTemplate redisTemplate;
public boolean isAllowed(String ip, int windowSize, int maxRequests) {
String key = "rate_limit:ip:" + ip;
long now = System.currentTimeMillis();
long window = now - windowSize * 1000;
// 使用Lua脚本保证原子性
String luaScript = """
local key = KEYS[1]
local window = ARGV[1]
local now = ARGV[2]
local maxRequests = ARGV[3]
-- 删除过期记录
redis.call('zremrangebyscore', key, 0, window)
-- 统计当前窗口内的请求数
local current = redis.call('zcard', key)
if current < tonumber(maxRequests) then
redis.call('zadd', key, now, now)
redis.call('expire', key, tonumber(ARGV[4]))
return 1
else
return 0
end
""";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(key),
String.valueOf(window),
String.valueOf(now),
String.valueOf(maxRequests),
String.valueOf(windowSize));
return result != null && result == 1;
}
}
方案3:分层限流 - 专业装备
适用场景:大流量系统,需要精细化控制
@Component
public class TieredIpRateLimiter {
// 限流规则配置
private static final Map<String, RateLimitRule> RULES = Map.of(
"api", new RateLimitRule(100, 60), // API接口:每分钟100次
"login", new RateLimitRule(5, 300), // 登录接口:每5分钟5次
"register", new RateLimitRule(3, 3600) // 注册接口:每小时3次
);
public boolean isAllowed(String ip, String endpoint) {
RateLimitRule rule = RULES.get(endpoint);
if (rule == null) return true;
// 检查黑白名单
if (isInBlacklist(ip)) return false;
if (isInWhitelist(ip)) return true;
// 应用限流规则
return applyRateLimit(ip, endpoint, rule);
}
@Data
@AllArgsConstructor
public static class RateLimitRule {
private int maxRequests;
private int windowSize;
}
}
方案4:智能动态限流 - 高级装备
适用场景:高流量系统,需要智能防护
@Component
public class SmartIpRateLimiter {
public boolean smartRateLimit(String ip, String endpoint) {
// 1. 获取系统当前负载
double systemLoad = getSystemLoad();
// 2. 获取IP的历史行为评分
double ipScore = getIpBehaviorScore(ip);
// 3. 动态计算限流阈值
int baseLimit = getBaseLimit(endpoint);
int dynamicLimit = calculateDynamicLimit(baseLimit, systemLoad, ipScore);
// 4. 应用限流
return applyDynamicRateLimit(ip, endpoint, dynamicLimit);
}
private double getSystemLoad() {
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
return osBean.getProcessCpuLoad();
}
private int calculateDynamicLimit(int baseLimit, double systemLoad, double ipScore) {
// 系统负载越高,限流越严格
double loadFactor = 1.0 - systemLoad * 0.5;
// IP评分越低,限流越严格
double scoreFactor = ipScore;
int dynamicLimit = (int) (baseLimit * loadFactor * scoreFactor);
return Math.max(1, dynamicLimit);
}
}
方案5:多维度综合限流 - 王者装备
适用场景:超大流量系统,需要全方位防护
@Component
public class ComprehensiveRateLimiter {
public RateLimitResult isAllowed(RateLimitRequest request) {
List<RateLimitResult> results = new ArrayList<>();
// 1. IP维度限流
results.add(checkIpRateLimit(request));
// 2. 用户维度限流
if (request.getUserId() != null) {
results.add(checkUserRateLimit(request));
}
// 3. 设备指纹限流
if (request.getDeviceFingerprint() != null) {
results.add(checkDeviceRateLimit(request));
}
// 4. 综合判断
return combineResults(results);
}
private RateLimitResult combineResults(List<RateLimitResult> results) {
// 任何一个维度被限制,则整体被限制
for (RateLimitResult result : results) {
if (!result.isAllowed()) {
return result;
}
}
return RateLimitResult.allow();
}
}
三、实战案例:某电商平台的反爬虫之战
背景
某电商平台每天遭受大量爬虫攻击,商品价格信息被大量抓取,服务器负载居高不下,正常用户体验受到严重影响。
解决方案
阶段一:基础防护
@Component
public class AntiCrawlerService {
public boolean basicIpLimit(String ip) {
// 普通用户:每分钟最多60次请求
// 可疑IP:每分钟最多10次请求
int limit = isSuspiciousIp(ip) ? 10 : 60;
return simpleRateLimit(ip, 60, limit);
}
private boolean isSuspiciousIp(String ip) {
return isProxyIp(ip) || hasAbnormalBehavior(ip);
}
}
阶段二:行为分析
@Component
public class BehaviorAnalyzer {
public CrawlerRisk analyzeBehavior(String ip, HttpServletRequest request) {
CrawlerRisk risk = new CrawlerRisk();
// 1. User-Agent分析
String userAgent = request.getHeader("User-Agent");
if (isSuspiciousUserAgent(userAgent)) {
risk.addScore(20);
}
// 2. 请求频率分析
if (getRequestRate(ip) > 50) {
risk.addScore(30);
}
return risk;
}
}
效果
经过优化:
- 爬虫流量从80%降至20%
- 服务器CPU使用率从90%降至40%
- 正常用户响应时间从3秒降至800ms
- 月度带宽成本节省60%
四、IP限流的最佳实践
1. 分层防护策略
CDN → 防火墙 → 负载均衡 → 网关 → 应用
2. 多维度结合
不要只依赖IP,要结合:
- 用户行为模式
- 设备指纹
- 地理位置
- 时间窗口
3. 优雅降级
被限流时要给用户友好的提示:
@ControllerAdvice
public class RateLimitExceptionHandler {
@ExceptionHandler(RateLimitException.class)
public ResponseEntity<?> handleRateLimit(RateLimitException e) {
return ResponseEntity.status(429).body(Map.of(
"error", "请求过于频繁",
"message", "请稍后再试",
"retryAfter", e.getRetryAfter()
));
}
}
4. 关键监控指标
- IP限流触发次数
- 黑名单IP数量
- 限流误伤率
- 系统响应时间
五、踩坑经验分享
坑1:忽略IPv6
很多同学只考虑IPv4,忘记了IPv6:
public boolean isValidIp(String ip) {
return isValidIPv4(ip) || isValidIPv6(ip);
}
坑2:代理IP获取错误
要考虑多层代理的情况:
// X-Forwarded-For: client, proxy1, proxy2
String[] ips = xForwardedFor.split(",");
String clientIp = ips[0].trim(); // 第一个是真实客户端IP
坑3:误伤正常用户
- 公司WiFi出口IP被限制,整个公司受影响
- 小区宽带共享IP被封,整个小区用户受影响
解决方案:设置白名单,建立申诉机制
结语
IP限流是系统安全的第一道防线,但不是万能药。真正的安全防护需要:
- 多层防护:从网络层到应用层的全方位保护
- 智能识别:结合行为分析,精准识别恶意请求
- 动态调整:根据业务特点和系统负载动态优化
- 优雅降级:在保护系统的同时,不影响正常用户体验
记住老司机的一句话:"最好的限流,是让坏人感受到限制,让好人感受到保护"。
觉得有用的话,点赞、在看、转发三连走起!下期我们聊聊如何设计一个高可用的分布式锁,敬请期待~
关注公众号:服务端技术精选
每周分享后端架构设计的实战经验,让技术更有温度!
标题:IP限流又双叒叕被绕过了?这5种实战方案让恶意请求无处遁形!
作者:jiangyi
地址:http://www.jiangyi.space/articles/2025/12/21/1766304296036.html