本文目录导读:

- IP 白名单(最常用、最简单)
- Token/API Key 白名单(适用于 API 场景)
- User-Agent 白名单(适用于爬虫或特定客户端)
- 组合使用多种豁免条件(推荐)
- 配置文件管理临时白名单(运营人员使用)
- 最佳实践建议
- 简单的实现示例(完整的中间件)
在 PHP 项目中对白名单进行限流豁免(临时放行),通常有几种常见的实现方案,以下是按推荐程度和复杂度排序的几种方法:
IP 白名单(最常用、最简单)
方案 A:在限流中间件中直接判断
<?php
// 限流中间件伪代码
class RateLimitMiddleware {
// 白名单 IP 列表(可以从配置文件、数据库或 Redis 读取)
private $whitelist = [
'127.0.0.1',
'10.0.0.1',
'192.168.1.100',
// 也可以支持 CIDR 格式
'10.0.0.0/8',
];
public function handle($request) {
$clientIp = $request->getClientIp();
// 先检查是否在白名单中
if ($this->isWhitelisted($clientIp)) {
return true; // 直接放行,不进行限流
}
// 正常限流逻辑
if ($this->exceedsLimit($request)) {
throw new RateLimitException('Too Many Requests');
}
return true;
}
private function isWhitelisted($ip) {
foreach ($this->whitelist as $whitelistItem) {
// 支持 CIDR 匹配
if (strpos($whitelistItem, '/') !== false) {
if ($this->ipInCidr($ip, $whitelistItem)) {
return true;
}
} else {
if ($ip === $whitelistItem) {
return true;
}
}
}
return false;
}
private function ipInCidr($ip, $cidr) {
list($subnet, $mask) = explode('/', $cidr);
$ipLong = ip2long($ip);
$subnetLong = ip2long($subnet);
$maskLong = -1 << (32 - $mask);
return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
}
}
方案 B:使用 Redis 存储动态白名单(更灵活)
<?php
class DynamicWhitelist {
private $redis;
private $whitelistKey = 'rate_limit:whitelist';
public function __construct($redis) {
$this->redis = $redis;
}
// 临时添加白名单(带过期时间)
public function addTemporaryWhitelist($ip, $ttl = 3600) {
$this->redis->sadd($this->whitelistKey, $ip);
$this->redis->expire($this->whitelistKey, $ttl); // 整个集合过期
// 或者使用单独的 key 带过期
$this->redis->setex("whitelist:{$ip}", $ttl, 1);
}
public function isWhitelisted($ip) {
// 检查动态白名单
if ($this->redis->exists("whitelist:{$ip}")) {
return true;
}
// 也支持静态白名单
return in_array($ip, $this->getStaticWhitelist());
}
// 移除白名单
public function removeWhitelist($ip) {
$this->redis->del("whitelist:{$ip}");
}
}
Token/API Key 白名单(适用于 API 场景)
<?php
class TokenWhitelist {
// 临时发放的豁免 Token
private $exemptTokens = [
'temp-token-abc123' => [
'expires_at' => 1700000000,
'limit' => 10000, // 可以设置特殊限额
],
'emergency-token-xyz789' => [
'expires_at' => 1700100000,
'unlimited' => true, // 完全不限流
]
];
public function checkToken($token) {
if (!isset($this->exemptTokens[$token])) {
return false;
}
$tokenData = $this->exemptTokens[$token];
// 检查过期
if (isset($tokenData['expires_at']) && time() > $tokenData['expires_at']) {
unset($this->exemptTokens[$token]); // 自动清理过期 token
return false;
}
return true;
}
}
User-Agent 白名单(适用于爬虫或特定客户端)
<?php
class UserAgentWhitelist {
private $whitelistPatterns = [
'/^Googlebot/i',
'/^Baiduspider/i',
'/^MyCompanyCrawler/i',
'/^Emergency\sClient/i', // 临时救急用
];
public function isWhitelisted($userAgent) {
foreach ($this->whitelistPatterns as $pattern) {
if (preg_match($pattern, $userAgent)) {
return true;
}
}
return false;
}
}
组合使用多种豁免条件(推荐)
<?php
class RateLimitExemptionManager {
private $whitelist;
private $tokenWhitelist;
private $userAgentWhitelist;
private $redis;
public function shouldExempt($request) {
// 1. 检查 IP 白名单
if ($this->whitelist->isWhitelisted($request->getClientIp())) {
return true;
}
// 2. 检查 API Token
$token = $request->getHeader('X-Exempt-Token');
if ($token && $this->tokenWhitelist->checkToken($token)) {
return true;
}
// 3. 检查 User-Agent
$ua = $request->getHeader('User-Agent');
if ($ua && $this->userAgentWhitelist->isWhitelisted($ua)) {
return true;
}
// 4. 检查 Cookie 或 Session(用于调试)
if ($request->getCookie('debug_mode') === 'whitelist') {
return true;
}
return false;
}
// 临时添加豁免
public function addTemporaryExemption($type, $value, $duration = 3600) {
switch ($type) {
case 'ip':
$this->redis->setex("exempt:ip:{$value}", $duration, 1);
break;
case 'token':
$this->redis->setex("exempt:token:{$value}", $duration, json_encode([
'limit' => 5000,
'created_at' => time()
]));
break;
}
}
}
配置文件管理临时白名单(运营人员使用)
config/whitelist.php
<?php
return [
// 永久白名单
'permanent' => [
'127.0.0.1',
'10.0.0.0/8',
],
// 临时白名单(带到期时间)
'temporary' => [
'192.168.1.50' => [
'expires_at' => '2024-12-31 23:59:59',
'note' => '运维人员临时调试'
],
'10.0.0.200' => [
'expires_at' => '+2 hours', // 支持相对时间
'note' => '紧急数据修复'
]
],
// 特殊规则
'special_routes' => [
'/api/health' => 'unlimited', // 健康检查端点不限流
'/api/emergency/**' => [ // 紧急接口特殊处理
'limit' => 1000,
'window' => 60
]
]
];
最佳实践建议
分层检查:IP → Token → User-Agent → 特殊路由
动态管理:通过 API 接口动态添加/移除临时白名单
// 管理接口示例
Route::post('/admin/whitelist/add', function() {
$ip = request('ip');
$duration = request('duration', 3600);
$reason = request('reason', '');
RateLimitExemptionManager::addTemporaryExemption('ip', $ip, $duration);
// 记录操作日志
Log::info("Added whitelist exemption", [
'ip' => $ip,
'duration' => $duration,
'reason' => $reason,
'operator' => auth()->user()->id
]);
return response()->json(['status' => 'ok']);
});
设置过期时间:临时放行一定要有 TTL(Time To Live),避免忘记清理
日志审计:记录所有白名单添加/移除操作
限制滥用:白名单本身也应该限流,防止被滥用
使用 Redis 或数据库:避免每次重启服务都丢失临时白名单
简单的实现示例(完整的中间件)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class RateLimitWithWhitelist
{
public function handle(Request $request, Closure $next)
{
$ip = $request->ip();
// 1. 检查白名单
if ($this->isWhitelisted($ip)) {
return $next($request);
}
// 2. 正常限流(这里示例简单的计数器)
$key = "rate_limit:{$ip}:" . $request->path();
$count = Cache::get($key, 0);
if ($count >= 60) { // 每分钟60次
return response('Too Many Requests', 429);
}
Cache::put($key, $count + 1, 60);
return $next($request);
}
private function isWhitelisted($ip)
{
// 从缓存读取白名单(可以动态管理)
$whitelist = Cache::get('rate_limit_whitelist', []);
if (in_array($ip, $whitelist)) {
return true;
}
// 检查 CIDR
foreach ($whitelist as $item) {
if (strpos($item, '/') !== false) {
if ($this->ipInCidr($ip, $item)) {
return true;
}
}
}
return false;
}
}
选择哪种方案取决于你的项目架构和安全需求,最简单可靠的是 IP 白名单 + Redis 动态管理,既能快速响应,又便于审计和自动过期清理。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播