W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
限流類(Throttler)提供了一種非常簡(jiǎn)單的方法,可以將用戶要執(zhí)行的活動(dòng)限制為在設(shè)定的時(shí)間段內(nèi)只能進(jìn)行一定次數(shù)的嘗試。 這最常用于對(duì) API 進(jìn)行速率限制,或限制用戶針對(duì)表單進(jìn)行的嘗試次數(shù),以幫助防止暴力攻擊。 該類可用于你根據(jù)設(shè)置的時(shí)間來進(jìn)行限制的操作。
Throttler 實(shí)現(xiàn)了 Token Bucket (令牌桶) 算法的一個(gè)簡(jiǎn)化版本。一般,會(huì)將你要執(zhí)行的每個(gè)操作都視為一個(gè)存儲(chǔ)桶。調(diào)用該 check()
方法時(shí),你要告訴它存儲(chǔ)桶的大小, 可以容納多少令牌以及時(shí)間間隔。在默認(rèn)情況下,每個(gè) check()
的調(diào)用請(qǐng)求將會(huì)使用1個(gè)可用令牌。讓我們通過一個(gè)例子來闡明這一點(diǎn)。 (譯注:國內(nèi)用戶可參考 令牌桶 )
假設(shè)我們希望某動(dòng)作每秒發(fā)生一次。對(duì) Throttler 的第一次呼叫將如下所示。第一個(gè)參數(shù)是存儲(chǔ)桶名稱,第二個(gè)參數(shù)是存儲(chǔ)桶持有的令牌數(shù)量, 第三個(gè)參數(shù)是存儲(chǔ)桶重新填充所需的時(shí)間:
$throttler = \Config\Services::throttler();
$throttler->check($name, 60, MINUTE);
我們暫時(shí)使用 全局常量 </general/common_functions> 的其中一個(gè),以使其更具可讀性。也就是說,這個(gè)存儲(chǔ)桶每分鐘允許執(zhí)行60次操作, 或者每秒允許執(zhí)行1次操作。
假設(shè)某個(gè)第三方腳本試圖重復(fù)訪問 URL 。最初,它能夠在不到一秒鐘的時(shí)間內(nèi)使用完所有60個(gè)令牌。但是,在那之后, Throttler 將僅允許每秒執(zhí)行一次操作,從而有可能減慢請(qǐng)求的速度,以至于讓攻擊不再有價(jià)值。
注解
為了使 Throttler 類可以正常工作,必須將 Cache 庫設(shè)置為實(shí)際可用的緩存對(duì)象處理程序。為了獲得最佳性能, 建議使用像 Redis 或 Memcached 那樣的內(nèi)存緩存。
Throttler 類不會(huì)自發(fā)地做任何的請(qǐng)求速率限制或?qū)φ?qǐng)求進(jìn)行限流,但卻是上述功能得以實(shí)現(xiàn)的關(guān)鍵。這里提供了一個(gè)示例 過濾器 , 該過濾器以每個(gè)IP地址每秒一個(gè)請(qǐng)求的速率限制實(shí)現(xiàn)了非常簡(jiǎn)單的速率限制。我們將介紹它的工作原理,以及如何設(shè)置它并開始在應(yīng)用程序中使用它。
你可以在 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
{
/**
* 這是一個(gè)為應(yīng)用程序使用 Trottler 類來實(shí)現(xiàn)速率限制的實(shí)例
*
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
*
* @return mixed
*/
public function before(RequestInterface $request)
{
$throttler = Services::throttler();
// 在整個(gè)站點(diǎn)上將IP地址限制為每秒不超過1個(gè)請(qǐng)求
if ($throttler->check($request->getIPAddress(), 60, MINUTE) === false)
{
return Services::response()->setStatusCode(429);
}
}
//--------------------------------------------------------------------
/**
* 暫時(shí)無事可做
*
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
*
* @return mixed
*/
public function after(RequestInterface $request, ResponseInterface $response)
{
}
}
運(yùn)行時(shí),此方法首先獲取節(jié)流閥的實(shí)例。接下來,它將IP地址用作存儲(chǔ)桶名稱,并進(jìn)行設(shè)置以將其限制為每秒一個(gè)請(qǐng)求。 如果節(jié)流閥拒絕檢查,返回false,則我們返回一個(gè)狀態(tài)碼為429(太多嘗試的 HTTP Response)的響應(yīng), 并且腳本執(zhí)行在調(diào)用控制器之前就結(jié)束了。本示例將基于對(duì)站點(diǎn)的所有請(qǐng)求(而不是每頁)中的單個(gè)IP地址進(jìn)行限制。
我們不一定需要限制網(wǎng)站上的每個(gè)頁面。對(duì)于許多Web應(yīng)用程序,最有意義的是僅將其應(yīng)用于POST請(qǐng)求,盡管API可能希望限制用戶發(fā)出的每個(gè)請(qǐng)求。 為了將此應(yīng)用到傳入請(qǐng)求,你需要編輯 /app/Config/Filters.php 并首先向過濾器添加別名:
public $aliases = [
...
'throttle' => \App\Filters\Throttle::class
];
接下來,我們將其分配給網(wǎng)站上的所有POST請(qǐng)求:
public $methods = [
'post' => ['throttle', 'CSRF']
];
這就是全部?,F(xiàn)在,會(huì)對(duì)網(wǎng)站上發(fā)出的所有POST請(qǐng)求進(jìn)行速率限制。
check
(string $key, int $capacity, int $seconds[, int $cost = 1])
參數(shù): | $key (string) – 儲(chǔ)存桶的名稱 |
---|---|
$capacity (int) – 儲(chǔ)存桶中持有的令牌數(shù)量 | |
$seconds (int) – 儲(chǔ)存桶完全填滿的秒數(shù) | |
$cost (int) – 此操作將會(huì)花費(fèi)的令牌數(shù)量 | |
返回: | 如果可以執(zhí)行此操作則為 TRUE,否則為 FALSE |
返回類型: | bool |
檢查存儲(chǔ)桶中是否還有令牌,或者是否在分配的時(shí)間限制內(nèi)使用了太多令牌。在每次檢查期間,如果成功,將根據(jù) $cost 參數(shù)來減少可用令牌的數(shù)量 。
getTokentime
()
返回: | 直到下一次令牌可用的秒數(shù) |
---|---|
返回類型: | int |
在 check()
運(yùn)行并返回 FALSE 之后,可以使用此方法確定直到新令牌可用并可以再次嘗試操作之前的時(shí)間。 在這種情況下,最小強(qiáng)制等待時(shí)間為一秒。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: