CodeIgniter4 限流類

2020-08-17 17:02 更新

限流類(Throttler)提供了一種非常簡單的方法,可以將用戶要執(zhí)行的活動限制為在設(shè)定的時間段內(nèi)只能進行一定次數(shù)的嘗試。 這最常用于對 API 進行速率限制,或限制用戶針對表單進行的嘗試次數(shù),以幫助防止暴力攻擊。 該類可用于你根據(jù)設(shè)置的時間來進行限制的操作。

總覽

Throttler 實現(xiàn)了 Token Bucket (令牌桶) 算法的一個簡化版本。一般,會將你要執(zhí)行的每個操作都視為一個存儲桶。調(diào)用該 check() 方法時,你要告訴它存儲桶的大小, 可以容納多少令牌以及時間間隔。在默認情況下,每個 check() 的調(diào)用請求將會使用1個可用令牌。讓我們通過一個例子來闡明這一點。 (譯注:國內(nèi)用戶可參考 令牌桶

假設(shè)我們希望某動作每秒發(fā)生一次。對 Throttler 的第一次呼叫將如下所示。第一個參數(shù)是存儲桶名稱,第二個參數(shù)是存儲桶持有的令牌數(shù)量, 第三個參數(shù)是存儲桶重新填充所需的時間:

$throttler = \Config\Services::throttler();
$throttler->check($name, 60, MINUTE);

我們暫時使用 全局常量 </general/common_functions> 的其中一個,以使其更具可讀性。也就是說,這個存儲桶每分鐘允許執(zhí)行60次操作, 或者每秒允許執(zhí)行1次操作。

假設(shè)某個第三方腳本試圖重復(fù)訪問 URL 。最初,它能夠在不到一秒鐘的時間內(nèi)使用完所有60個令牌。但是,在那之后, Throttler 將僅允許每秒執(zhí)行一次操作,從而有可能減慢請求的速度,以至于讓攻擊不再有價值。

注解

為了使 Throttler 類可以正常工作,必須將 Cache 庫設(shè)置為實際可用的緩存對象處理程序。為了獲得最佳性能, 建議使用像 Redis 或 Memcached 那樣的內(nèi)存緩存。

速率限制

Throttler 類不會自發(fā)地做任何的請求速率限制或?qū)φ埱筮M行限流,但卻是上述功能得以實現(xiàn)的關(guān)鍵。這里提供了一個示例 過濾器 , 該過濾器以每個IP地址每秒一個請求的速率限制實現(xiàn)了非常簡單的速率限制。我們將介紹它的工作原理,以及如何設(shè)置它并開始在應(yīng)用程序中使用它。

實現(xiàn)代碼

你可以在 app/Filters/Throttle.php 上創(chuàng)建自己的Throttler過濾器,大致如下:

<?php namespace App\Filters;


use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Services;


class Throttle implements FilterInterface
{
        /**
         * 這是一個為應(yīng)用程序使用 Trottler 類來實現(xiàn)速率限制的實例
         *
         * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
         *
         * @return mixed
         */
        public function before(RequestInterface $request)
        {
            $throttler = Services::throttler();


                    // 在整個站點上將IP地址限制為每秒不超過1個請求
                    if ($throttler->check($request->getIPAddress(), 60, MINUTE) === false)
            {
                return Services::response()->setStatusCode(429);
                    }
        }


        //--------------------------------------------------------------------


        /**
         * 暫時無事可做
         *
         * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
         * @param ResponseInterface|\CodeIgniter\HTTP\Response       $response
         *
         * @return mixed
         */
        public function after(RequestInterface $request, ResponseInterface $response)
        {
        }
}

運行時,此方法首先獲取節(jié)流閥的實例。接下來,它將IP地址用作存儲桶名稱,并進行設(shè)置以將其限制為每秒一個請求。 如果節(jié)流閥拒絕檢查,返回false,則我們返回一個狀態(tài)碼為429(太多嘗試的 HTTP Response)的響應(yīng), 并且腳本執(zhí)行在調(diào)用控制器之前就結(jié)束了。本示例將基于對站點的所有請求(而不是每頁)中的單個IP地址進行限制。

應(yīng)用過濾器

我們不一定需要限制網(wǎng)站上的每個頁面。對于許多Web應(yīng)用程序,最有意義的是僅將其應(yīng)用于POST請求,盡管API可能希望限制用戶發(fā)出的每個請求。 為了將此應(yīng)用到傳入請求,你需要編輯 /app/Config/Filters.php 并首先向過濾器添加別名:

public $aliases = [
        ...
        'throttle' => \App\Filters\Throttle::class
];

接下來,我們將其分配給網(wǎng)站上的所有POST請求:

public $methods = [
    'post' => ['throttle', 'CSRF']
];

這就是全部?,F(xiàn)在,會對網(wǎng)站上發(fā)出的所有POST請求進行速率限制。

類參考

check(string $key, int $capacity, int $seconds[, int $cost = 1])

參數(shù): $key (string) – 儲存桶的名稱
$capacity (int) – 儲存桶中持有的令牌數(shù)量
$seconds (int) – 儲存桶完全填滿的秒數(shù)
$cost (int) – 此操作將會花費的令牌數(shù)量
返回: 如果可以執(zhí)行此操作則為 TRUE,否則為 FALSE
返回類型: bool

檢查存儲桶中是否還有令牌,或者是否在分配的時間限制內(nèi)使用了太多令牌。在每次檢查期間,如果成功,將根據(jù) $cost 參數(shù)來減少可用令牌的數(shù)量 。

getTokentime()

返回: 直到下一次令牌可用的秒數(shù)
返回類型: int

check() 運行并返回 FALSE 之后,可以使用此方法確定直到新令牌可用并可以再次嘗試操作之前的時間。 在這種情況下,最小強制等待時間為一秒。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號