제한기

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는 1초에 최대 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: '각 사용자는 하루에 한 번만 쿠폰을 받을 수 있습니다.')]
    public function coupon(): string
    {
        // key: 'coupon', 여기서 coupon은 사용자 정의 key로, 전역적으로 coupon을 기준으로 제한하며, 하루에 최대 100개의 쿠폰을 발급합니다.
        // 동시에 사용자 ID를 기준으로 제한하여, 각 사용자는 하루에 한 번만 쿠폰을 받을 수 있습니다.
        return '쿠폰 발송 성공';
    }

    #[RateLimiter(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: '각 전화번호는 하루에 최대 5개의 SMS를 받을 수 있습니다.')]
    public function sendSms2(): string
    {
        // key가 변수를 사용할 때, [클래스, 정적 메소드]의 방식으로 key를 가져올 수 있습니다. 예를 들어 [UserController::class, 'getMobile']는 UserController의 getMobile() 메소드를 호출하여 반환되는 값을 key로 사용합니다.
        return 'SMS 발송 성공';
    }

    /**
     * 사용자 정의 key, 전화번호 가져오기, 정적 메소드여야 합니다.
     * @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 제한(세션 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, '각 전화번호는 하루에 최대 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: auto, apcu, memory, redis 중 하나의 값으로, auto를 사용할 경우 apcumemory 중 하나를 자동으로 선택합니다.
  • 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 명령을 통해 php.ini의 위치를 찾을 수 있습니다.

  • 소개
    성능은 memory에 비해 약간 낮지만, 다중 프로세스로 제한 데이터를 공유할 수 있습니다.

  • 사용 제한
    클러스터를 지원하지 않습니다.

  • 적합한 사용 사례
    모든 개발 환경; 온라인 단일 머신 제한 환경; 클러스터에서 엄격한 제한이 필요 없는 경우; CC 공격 방어.

redis

  • 의존성
    redis 확장을 설치하고 Redis 구성 요소를 설치해야 합니다. 설치 명령은 다음과 같습니다.

    composer require -W illuminate/redis illuminate/events
  • 소개
    성능은 apcu보다 낮으며, 단일 머신 및 클러스터 기반의 정밀 제한을 지원합니다.

  • 적합한 사용 사례
    개발 환경; 온라인 단일 머신 환경; 클러스터 환경