Rate Limiter

Der Webman Rate Limiter unterstützt Annotation-Rate-Limiting.
Er unterstützt die Treiber apcu, redis und memory.

Quellcode-Adresse

https://github.com/webman-php/rate-limiter

Installation

composer require webman/rate-limiter

Nutzung

<?php
namespace app\controller;

use RuntimeException;
use Webman\RateLimiter\Annotation\RateLimiter;

class UserController
{

    #[RateLimiter(limit: 10)]
    public function index(): string
    {
        // Standardmäßig Limiting nach IP, die Standard-Zeitspanne beträgt 1 Sekunde
        return 'Maximal 10 Anfragen pro Sekunde und IP';
    }

    #[RateLimiter(limit: 100, ttl: 60, key: RateLimiter::UID)]
    public function search(): string
    {
        // key: RateLimiter::UID, Limiting basierend auf der Benutzer-ID, `session('user.id')` darf nicht leer sein
        return 'Maximal 100 Suchen alle 60 Sekunden pro Benutzer';
    }

    #[RateLimiter(limit: 1, ttl: 60, key: RateLimiter::SID, message: 'Jeder kann nur einmal pro Minute eine E-Mail senden')]
    public function sendMail(): string
    {
        // key: RateLimiter::SID, Limiting basierend auf der session_id
        return 'E-Mail erfolgreich gesendet';
    }

    #[RateLimiter(limit: 100, ttl: 24*60*60, key: 'coupon', message: 'Die Coupons für heute sind bereits vergeben, bitte kommen Sie morgen wieder')]
    #[RateLimiter(limit: 1, ttl: 24*60*60, key: RateLimiter::UID, message: 'Jeder Benutzer kann nur einmal täglich einen Coupon erhalten')]
    public function coupon(): string
    {
        // key: 'coupon', hier ist der Coupon ein benutzerdefinierter Schlüssel, der global Limiting mit dem Schlüssel coupon durchführt, maximal 100 Coupons pro Tag
        // Gleichzeitig wird nach Benutzer-ID limitiert, jeder Benutzer kann täglich nur einen Coupon erhalten
        return 'Coupon erfolgreich versendet';
    }

    #[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: 'Maximal 5 SMS pro Tag pro Telefonnummer')]
    public function sendSms2(): string
    {
        // Wenn der Schlüssel eine Variable ist, kann er im Format [Klasse, Statische Methode] abgerufen werden, z.B. [UserController::class, 'getMobile'] ruft den Rückgabewert der Methode getMobile() der UserController-Klasse ab
        return 'SMS erfolgreich gesendet';
    }

    /**
     * Benutzerdefinierter Schlüssel, um die Telefonnummer zu erhalten, muss eine statische Methode sein
     * @return string
     */
    public static function getMobile(): string
    {
        return request()->get('mobile');
    }

    #[RateLimiter(limit: 1, ttl: 10, key: RateLimiter::IP, message: 'Frequenz eingeschränkt', exception: RuntimeException::class)]
    public function testException(): string
    {
        // Bei Überschreitung wird standardmäßig die Ausnahme Webman\RateLimiter\RateLimitException ausgelöst, die über den Parameter exception geändert werden kann
        return 'ok';
    }

}

Hinweise

  • Der Standard-Zeitintervall beträgt 1 Sekunde.
  • Der Zeitintervall kann mit ttl eingestellt werden, z.B. ttl:60 für 60 Sekunden.
  • Die Standard-Limiting-Dimension ist IP-Limiting (standardmäßig bleibt 127.0.0.1 unbegrenzt, siehe Abschnitt zur Konfiguration weiter unten).
  • Eingebaute IP-Limiting, UID-Limiting (erfordert, dass session('user.id') nicht leer ist), SID-Limiting (mithilfe der session_id).
  • Wenn Nginx als Proxy verwendet wird, muss der Header X-Forwarded-For für IP-Limiting übergeben werden, siehe nginx proxy.
  • Bei Überschreitung wird die Ausnahme Webman\RateLimiter\RateLimitException ausgelöst, die über exception:xx eine benutzerdefinierte Ausnahme-Klasse zulässt.
  • Bei Auslösung einer Ausnahme bei Überschreitung ist die Standard-Fehlermeldung Too Many Requests, die über message:xx angepasst werden kann.
  • Die Standard-Fehlermeldung kann auch über Übersetzungen geändert werden, verwenden Sie dazu die folgenden Linux-Befehle:
    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

Schnittstelle

Manchmal möchten Entwickler den Rate Limiter direkt im Code aufrufen, siehe den folgenden Code

<?php
namespace app\controller;

use RuntimeException;
use Webman\RateLimiter\Limiter;

class UserController {

    public function sendSms(string $mobile): string
    {
        // Hier ist mobile der Schlüssel
        Limiter::check($mobile, 5, 24*60*60, 'Maximal 5 SMS pro Tag pro Telefonnummer');
        return 'SMS erfolgreich gesendet';
    }
}

Konfiguration

config/plugin/webman/rate-limiter/app.php

<?php
return [
    'enable' => true,
    'driver' => 'auto', // auto, apcu, memory, redis
    'stores' => [
        'redis' => [
            'connection' => 'default',
        ]
    ],
    // Diese IPs werden nicht auf Frequenz begrenzt (nur wenn der Schlüssel RateLimiter::IP ist)
    'ip_whitelist' => [
        '127.0.0.1',
    ],
];
  • enable: Ob das Rate Limiting aktiviert ist.
  • driver: Einen der Werte auto apcu memory redis. Bei der Verwendung von auto wird automatisch ein Wert zwischen apcu und memory ausgewählt.
  • stores: Konfiguration für redis, connection entspricht dem entsprechenden Schlüssel in config/redis.php.
  • ip_whitelist: IPs in der Whitelist werden nicht limitiert (nur wirksam, wenn der Schlüssel RateLimiter::IP ist).

Auswahl des Treibers

memory

  • Einführung
    memory erfordert keine zusätzliche Installation und bietet die beste Leistung.

  • Nutzungseinschränkungen
    Limiting gilt nur für den aktuellen Prozess, die Limiting-Daten werden nicht zwischen mehreren Prozessen geteilt und unterstützen auch kein Cluster-Limiting.

  • Anwendungsfälle
    Windows-Entwicklungsumgebung; Geschäftslogik, die kein strenges Limiting benötigt; Abwehr von CC-Angriffen.

apcu

  • Erweiterungsinstallation
    Es muss die apcu-Erweiterung installiert und in der php.ini eingestellt werden:

    apc.enabled=1
    apc.enable_cli=1

    Falls der Standort der php.ini unbekannt ist, kann dieser mit dem Befehl php --ini festgestellt werden.

  • Einführung
    Die Leistung ist etwas niedriger als memory, unterstützt die gemeinsame Nutzung von Limiting-Daten über mehrere Prozesse.

  • Nutzungseinschränkungen
    Unterstützt kein Cluster.

  • Anwendungsfälle
    Beliebige Entwicklungsumgebung; Szenarien mit Einzelmaschinen-Limiting in der Produktionsumgebung; Clusterumgebungen, die kein strenges Limiting erfordern; Abwehr von CC-Angriffen.

redis

  • Abhängigkeit
    Es muss die redis-Erweiterung installiert und die Redis-Komponente installiert werden, Installationsbefehl:

    composer require -W illuminate/redis illuminate/events
  • Einführung
    Die Leistung ist niedriger als bei apcu, unterstützt sowohl Einzelmaschinen als auch Cluster-Limiting präzise.

  • Anwendungsfälle
    Entwicklungsumgebungen; Einzelmaschine in der Produktionsumgebung; Cluster-Umgebungen.