レートリミッター

Webmanレートリミッターは、アノテーションによるレートリミットをサポートしています。
APCu、Redis、Memoryドライバーに対応しています。

ソースコードのアドレス

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

インストール

composer require webman/rate-limiter

使用方法

<?php
namespace app\controller;

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

class UserController
{

    #[RateLimiter(limit: 10)]
    public function index(): string
    {
        // デフォルトはIPでのレートリミット、デフォルトの単位時間は1秒です
        return '各IPは毎秒最大10件のリクエストを送信できます';
    }

    #[RateLimiter(limit: 100, ttl: 60, key: RateLimiter::UID)]
    public function search(): string
    {
        // key: RateLimiter::UID、ユーザーIDを基準にレートリミットします。要求はsession('user.id')が空でない必要があります
        return '各ユーザーは60秒間に最大100回検索できます';
    }

    #[RateLimiter(limit: 1, ttl: 60, key: RateLimiter::SID, message: '各人は1分間に1回のみメールを送信できます')]
    public function sendMail(): string
    {
        // key: RateLimiter::SID、 session_idを基準にレートリミットします
        return 'メールは正常に送信されました';
    }

    #[RateLimiter(limit: 100, ttl: 24*60*60, key: 'coupon', message: '今日のクーポンはすでに配布済みです。明日再度お越しください')]
    #[RateLimiter(limit: 1, ttl: 24*60*60, key: RateLimiter::UID, message: '各ユーザーは1日1回のみクーポンを受け取れます')]
    public function coupon(): string
    {
        // key: 'coupon'、ここでcouponはカスタムキーであり、全体でcouponをキーとしてレートリミットします。最大で1日あたり100枚のクーポンを配布します
        // 同時にユーザーIDを基準にレートリミットしており、各ユーザーは1日1回のみクーポンを受け取れます
        return 'クーポンは正常に配布されました';
    }

    #[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: '各電話番号は1日に最大5件のSMSを受信できます')]
    public function sendSms2(): string
    {
        // keyが変数の場合、[クラス, 静的メソッド]の方法でkeyを取得できます。例えば[UserController::class, 'getMobile']はUserControllerのgetMobile()メソッドを呼び出し、その戻り値をkeyとして使用します
        return 'SMSは正常に送信されました';
    }

    /**
     * カスタムキーとして電話番号を取得します。静的メソッドでなければなりません
     * @return string
     */
    public static function getMobile(): string
    {
        return request()->get('mobile');
    }

    #[RateLimiter(limit: 1, ttl: 10, key: RateLimiter::IP, message: '頻度制限', exception: RuntimeException::class)]
    public function testException(): string
    {
        // 超過した場合、デフォルトの例外はWebman\RateLimiter\RateLimitExceptionです。exceptionパラメータで変更できます
        return 'ok';
    }

}

説明

  • デフォルトの単位時間間隔は1秒です。
  • ttlを使用して単位時間間隔を設定できます。例えば ttl:60 は60秒です。
  • デフォルトのレートリミットの基準はIPリミットです(デフォルトでは127.0.0.1はレートリミットされません。以下の設定部分を参照してください)。
  • 内蔵のIPリミット、UIDリミット(session('user.id')が空でない必要があります)、SIDリミット(session_idに基づくリミット)があります。
  • Nginxプロキシを使用している場合、IPリミット時にはX-Forwarded-Forヘッダーを渡す必要があります。詳しくはnginx代理を参照してください。
  • 超過時にはWebman\RateLimiter\RateLimitException例外が発生し、exception:xxを使用してカスタム例外クラスを指定できます。
  • 超過時のエラーメッセージはデフォルトでToo Many Requestsですが、message:xxを使用してカスタマイズ可能です。
  • デフォルトのエラーメッセージは多言語を通じて変更可能です。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

インターフェース

時折、開発者はコード内で直接レートリミッターを呼び出したいことがあります。以下のコードを参考にしてください。

<?php
namespace app\controller;

use RuntimeException;
use Webman\RateLimiter\Limiter;

class UserController {

    public function sendSms(string $mobile): string
    {
        // ここでmobileがkeyとして使用されます
        Limiter::check($mobile, 5, 24*60*60, '各電話番号は1日に最大5件のSMSを受信できます');
        return 'SMSは正常に送信されました';
    }
}

設定

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

<?php
return [
    'enable' => true,
    'driver' => 'auto', // auto, apcu, memory, redis
    'stores' => [
        'redis' => [
            'connection' => 'default',
        ]
    ],
    // これらのIPからのリクエストは頻度制限されません(keyがRateLimiter::IPのときのみ有効)
    'ip_whitelist' => [
        '127.0.0.1',
    ],
];
  • enable: レートリミッターを有効にするかどうか
  • driver: autoapcumemoryredisのいずれかの値を指定します。autoを使用する場合は、自動的にapcuまたはmemoryのいずれかを選択します。
  • stores: redisの設定。connectionconfig/redis.php内の対応するkeyに対応します。
  • ip_whitelist: ホワイトリスト内のIPはレートリミットされません(keyがRateLimiter::IPの時のみ有効)。

ドライバーの選択

memory

  • 紹介
    何の拡張もインストールする必要がなく、最高のパフォーマンスを発揮します。

  • 使用制限
    レートリミットは現在のプロセスにのみ有効であり、複数のプロセス間でレートリミットデータは共有されません。また、クラスターでのレートリミットもサポートされていません。

  • 適用シーン
    Windows開発環境;厳密なレートリミットを必要としないビジネス;CC攻撃からの防御。

apcu

  • 拡張のインストール
    apcu拡張をインストールする必要があり、php.iniに次の設定が必要です。

    apc.enabled=1
    apc.enable_cli=1

    php.iniの場所がわからない場合は、php --iniコマンドで確認できます。

  • 紹介
    performanceはmemoryよりわずかに低く、マルチプロセス間でレートリミットデータを共有します。

  • 使用制限
    クラスターはサポートされていません。

  • 適用シーン
    どの開発環境にも対応;オンライン単一ホストのレートリミットシーン;クラスターで厳密なレートリミットが必要ないシーン;CC攻撃からの防御。

redis

  • 依存関係
    redis拡張をインストールし、Redisコンポーネントをインストールする必要があります。インストールコマンドは次の通りです。

    composer require -W illuminate/redis illuminate/events
  • 紹介
    性能はapcuより低いですが、単独ホストとクラスターの両方で精度の高いレートリミットをサポートします。

  • 適用シーン
    開発環境;オンライン単一ホスト環境;クラスター環境