Limiteur de débit
Limiteur de débit webman avec support de limitation par annotation.
Prend en charge les pilotes apcu, redis et memory.
Dépôt source
https://github.com/webman-php/limiter
Installation
composer require webman/limiter
Utilisation
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\annotation\Limit;
class UserController
{
#[Limit(limit: 10)]
public function index(): string
{
// Par défaut limitation par IP, fenêtre temporelle par défaut 1 seconde
return 'Maximum 10 requêtes par IP par seconde';
}
#[Limit(limit: 100, ttl: 60, key: Limit::UID)]
public function search(): string
{
// key: Limit::UID, limitation par ID utilisateur, exige session('user.id') non vide
return 'Maximum 100 recherches par utilisateur par 60 secondes';
}
#[Limit(limit: 1, ttl: 60, key: Limit::SID, message: '1 seul e-mail par personne par minute')]
public function sendMail(): string
{
// key: Limit::SID, limitation par session_id
return 'E-mail envoyé avec succès';
}
#[Limit(limit: 100, ttl: 24*60*60, key: 'coupon', message: 'Coupons du jour épuisés, réessayez demain')]
#[Limit(limit: 1, ttl: 24*60*60, key: Limit::UID, message: 'Chaque utilisateur ne peut réclamer qu\'un coupon par jour')]
public function coupon(): string
{
// key: 'coupon', clé personnalisée pour limitation globale, max 100 coupons par jour
// Aussi limitation par ID utilisateur, chaque utilisateur un seul coupon par jour
return 'Coupon envoyé avec succès';
}
#[Limit(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: 'Maximum 5 SMS par numéro par jour')]
public function sendSms2(): string
{
// Quand key est variable: [classe, méthode_statique], ex. [UserController::class, 'getMobile'] utilise la valeur de retour de UserController::getMobile() comme clé
return 'SMS envoyé avec succès';
}
/**
* Clé personnalisée, obtenir le numéro de mobile, doit être une méthode statique
* @return string
*/
public static function getMobile(): string
{
return request()->get('mobile');
}
#[Limit(limit: 1, ttl: 10, key: Limit::IP, message: 'Débit limité', exception: RuntimeException::class)]
public function testException(): string
{
// Exception par défaut en cas de dépassement: support\limiter\RateLimitException, modifiable via paramètre exception
return 'ok';
}
}
Notes
- Utilise l'algorithme à fenêtre fixe
- Fenêtre temporelle ttl par défaut: 1 seconde
- Définir la fenêtre via ttl, ex.
ttl:60pour 60 secondes - Dimension de limitation par défaut: IP (par défaut
127.0.0.1non limité, voir configuration ci-dessous) - Intégré: limitation IP, UID (exige
session('user.id')non vide), SID (parsession_id) - Avec proxy nginx, passer l'en-tête
X-Forwarded-Forpour limitation IP, voir proxy nginx - Déclenche
support\limiter\RateLimitExceptionen cas de dépassement, classe d'exception personnalisée viaexception:xx - Message d'erreur par défaut en cas de dépassement:
Too Many Requests, message personnalisé viamessage:xx - Message d'erreur par défaut modifiable via traduction, référence Linux:
composer require symfony/translation
mkdir resource/translations/zh_CN/ -p
echo "<?php
return [
'Too Many Requests' => '请求频率受限'
];" > resource/translations/zh_CN/messages.php
php start.php restart
API
Parfois les développeurs veulent appeler le limiteur directement dans le code, voir l'exemple suivant:
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\Limiter;
class UserController {
public function sendSms(string $mobile): string
{
// mobile utilisé comme clé ici
Limiter::check($mobile, 5, 24*60*60, 'Maximum 5 SMS par numéro par jour');
return 'SMS envoyé avec succès';
}
}
Configuration
config/plugin/webman/limiter/app.php
<?php
use support\limiter\RateLimitException;
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
// Ces IP ne sont pas limitées (efficace uniquement quand key est Limit::IP)
'ip_whitelist' => [
'127.0.0.1',
],
'exception' => RateLimitException::class
];
- enable: Activer la limitation de débit
- driver: Une des valeurs
auto,apcu,memory,redis;autochoisit automatiquement entreapcu(prioritaire) etmemory - stores: Configuration Redis,
connectioncorrespond à la clé dansconfig/redis.php - ip_whitelist: Les IP en liste blanche ne sont pas limitées (efficace uniquement quand key est
Limit::IP)
Choix du driver
memory
-
Introduction
Aucune extension requise, meilleures performances. -
Limites
Limitation uniquement pour le processus actuel, pas de partage entre processus, limitation en cluster non supportée. -
Cas d'usage
Environnement de développement Windows; scénarios sans limitation stricte; défense contre attaques CC.
apcu
- Installation de l'extension
Extension apcu requise, paramètres php.ini:
apc.enabled=1
apc.enable_cli=1
Emplacement de php.ini via php --ini
-
Introduction
Excellentes performances, supporte le partage multi-processus. -
Limites
Cluster non supporté -
Cas d'usage
Tout environnement de développement; limitation serveur unique en production; cluster sans limitation stricte; défense contre attaques CC.
redis
- Dépendances
Extension redis et composant Redis requis, installation:
composer require -W webman/redis illuminate/events
-
Introduction
Performances inférieures à apcu, supporte limitation précise serveur unique et cluster -
Cas d'usage
Environnement de développement; serveur unique en production; environnement cluster