首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP Session 防止用戶頻繁刷新頁面:基於時間間隔的請求節流

     編輯:PHP 相關知識

核心代碼

<?php
// 頁面頂部最早位置引入 (輸出任何內容前)
session_start();

$minInterval = 3; // 最小請求間隔 (秒)

// 檢查上次訪問時間
if (isset($_SESSION['last_access']) && (time() - $_SESSION['last_access'] < $minInterval)) {
    // 觸發限流
    http_response_code(429);
    // 可選:返回 JSON 供 AJAX 處理
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
        header('Content-Type: application/json');
        echo json_encode([
            'error' => 'request_too_frequent',
            'message' => "操作過於頻繁,請等待 " . ($minInterval - (time() - $_SESSION['last_access'])) . " 秒後重試",
            'retry_after' => $minInterval - (time() - $_SESSION['last_access'])
        ]);
    } else {
        // 普通頁面輸出友好提示
        die("您刷新頁面太快了,請等待幾秒鐘後再試。");
    }
    exit;
}

// 更新本次訪問時間
$_SESSION['last_access'] = time();

// 正常業務邏輯繼續...
?>
<!DOCTYPE html>
<html>
<head><title>頁面內容</title></head>
<body>
    <h1>歡迎訪問</h1>
    <p>當前時間:<?php echo date('H:i:s'); ?></p>
</body>
</html>

機制原理

環節說明
session_start()啟用會話,依賴 Cookie PHPSESSID 識別用戶
$_SESSION['last_access']存儲上次成功請求的 Unix 時間戳
time() - last_access計算距上次請求經過秒數
小於 $minInterval判定為頻繁刷新,直接拒絕響應
更新 last_access僅允許請求通過時更新,被拒絕的請求不重置計時器

優缺點分析

優點缺點
代碼極簡,無需數據庫/Redis僅限單機/同 Session 存儲 (文件/數據庫/Redis 需共享 Session)
基於用戶會話,不影響不同用戶用戶清除 Cookie / 無痕模式 / 禁用 Cookie 即可繞過
可區分 AJAX 與普通請求返回不同格式無法防禦分布式爬蟲 (換 IP/換 Session)
靈活調整間隔時間Session 文件鎖可能導致併發請求阻塞 (session_write_close() 可緩解)

生產環境增強版

<?php
session_start();

// 配置
$config = [
    'min_interval' => 2,           // 秒
    'max_burst'    => 5,           // 允許突發請求數 (令牌桶)
    'block_time'   => 60,          // 封禁秒數
    'whitelist_ips' => ['127.0.0.1', '10.0.0.1'], // 白名單
];

// 白名單檢查
if (in_array($_SERVER['REMOTE_ADDR'], $config['whitelist_ips'])) {
    // 內部監控/測試 IP 放行
    goto BUSINESS_LOGIC;
}

// 令牌桶狀態鍵
$bucketKey = 'rate_bucket';
$now = time();

// 初始化桶
if (!isset($_SESSION[$bucketKey])) {
    $_SESSION[$bucketKey] = [
        'tokens' => $config['max_burst'],
        'last_refill' => $now,
        'blocked_until' => 0,
    ];
}
$bucket = &$_SESSION[$bucketKey]; // 引用修改

// 封禁檢查
if ($bucket['blocked_until'] > $now) {
    http_response_code(429);
    die("已觸發頻率限制,請等待 " . ($bucket['blocked_until'] - $now) . " 秒");
}

// 令牌回填 (每秒 1 個)
$elapsed = $now - $bucket['last_refill'];
if ($elapsed > 0) {
    $bucket['tokens'] = min($config['max_burst'], $bucket['tokens'] + $elapsed);
    $bucket['last_refill'] = $now;
}

// 消耗令牌
if ($bucket['tokens'] >= 1) {
    $bucket['tokens']--;
} else {
    // 觸發封禁
    $bucket['blocked_until'] = $now + $config['block_time'];
    http_response_code(429);
    die("操作過於頻繁,已暫時限制訪問 $config[block_time] 秒");
}

// 釋放 Session 鎖 (關鍵!避免併發阻塞)
session_write_close();

BUSINESS_LOGIC:
// 正常業務代碼...
?>

部署建議

  1. Session 存儲共享:多台 Web 服務器必須配置共享 Session (Redis/Memcached/Database),否則負載均衡下限流失效
  2. 靜態資源排除:Nginx 直接服務靜態文件 (.js/.css/.jpg),不經過 PHP,避免誤觸發限流
  3. 結合 Nginx limit_req:第一層在 Nginx 擋住惡意高頻洪峰,PHP 層做精細業務邏輯限流
  4. 監控告警:統計 429 響應率,異常升高時自動告警
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved