Laravel Best Practices — Ограничение частоты запросов API (Промежуточное ПО Throttle)

Redis PHP сервер Laravel

При предоставлении API в общедоступную сеть для внешнего доступа к данным, во избежание злонамеренных атак, лучше всего добавить в API ограничение частоты запросов в дополнение к аутентификации по токену.В Laravel начиная с 5.2 компонент Throttle, который приходит с фреймворком поддерживает ограничение частоты доступа. , и предоставляет промежуточное ПО Throttle для использования, но промежуточное ПО Throttle будет возвращать HTML-ответ, когда частота доступа к API достигает предела, чтобы сообщить вам о необходимости разгона. В приложениях мы часто предпочитают возвращать ответ API вместо ответа HTML, поэтому в статье будет предоставлено специальное промежуточное ПО, которое заменит промежуточное ПО Throttle по умолчанию для реализации пользовательского содержимого ответа.

Обзор ограничения частоты

Ограничение частоты часто используется в API для ограничения частоты запросов к определенному API отдельными запросчиками. Например, если установлено ограничение частоты 1000 в минуту, при превышении лимита в течение минуты сервер вернет429: Too Many Attempts.отклик.

Как правило, хорошо написанное приложение, реализующее ограничение частоты, также возвращает три заголовка ответа:X-RateLimit-Limit, X-RateLimit-RemainingиRetry-After(Retry-Afterзаголовки будут возвращены только после достижения лимита).X-RateLimit-Limitсообщает нам максимальное количество запросов, разрешенных в указанное время,X-RateLimit-RemainingОтносится к количеству запросов, оставшихся в указанный период времени,Retry-AfterОтносится ко времени (с) ожидания до следующего запроса на повторную попытку.

Примечание: Каждое приложение будет выбирать свой собственный временной интервал ограничения частоты.Временной интервал ограничения частоты доступа приложения Laravel составляет одну минуту, поэтому ограничение частоты ограничивает количество посещений в одну минуту.

Используйте промежуточное программное обеспечение дросселя

Давайте посмотрим на использование этого промежуточного программного обеспечения.Сначала мы определяем маршрут и добавляем к нему дроссель промежуточного программного обеспечения.По умолчанию дроссель ограничивает 60 попыток в минуту и ​​запрещает доступ после 60 посещений в минуту:

Route::group(['prefix'=>'api','middleware'=>'throttle'], function(){
    Route::get('users', function(){
        return \App\User::all();
    });
});

При доступе к маршруту /api/users в заголовке ответа вы увидите следующую информацию:

X-RateLimit-Limit: 60 X-RateLimit-Remaining: 58

Если запрошен разгон, он будет возвращен в заголовке ответа.Retry-After:

Retry-After: 58 X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0

Приведенная выше информация указывает на то, что доступ к странице или API вернется в нормальное состояние через 58 секунд.

Определите частоту и время ожидания повторных попыток

Частота по умолчанию – 60 раз. Вы можете указать желаемую частоту с помощью первого параметра промежуточного программного обеспечения дросселя. Время ожидания повторной попытки по умолчанию – одна минута. Вы можете указать желаемое количество минут с помощью второго параметра промежуточного программного обеспечения дросселирования.

Route::group(['prefix'=>'api','middleware'=>'throttle:5'],function(){
    Route::get('users',function(){
        return \App\User::all();
    });
});//频次上限5

Route::group(['prefix'=>'api','middleware'=>'throttle:5,10'],function(){
    Route::get('users',function(){
        return \App\User::all();
    });
});//频次上限5,重试等待时间10分钟

###Custom Throttle промежуточное ПО, возврат ответа API В дополнение к возврату этих заголовков ответа Throttle возвращает HTML-страницу, которая сообщает нам о слишком большом количестве попыток после того, как частота запросов достигает верхнего предела. При вызове API мы, очевидно, предпочитаем получать ответ в формате json.Ниже представлено пользовательское промежуточное ПО для замены промежуточного ПО Throttle по умолчанию для настройки информации ответа.

Сначала создайте промежуточное ПО ThrottleRequests:php artisan make:middleware ThrottleRequests.

Скопируйте приведенный ниже код вapp/Http/Middlewares/ThrottleReuqestsВ файле:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Cache\RateLimiter;
use Symfony\Component\HttpFoundation\Response;

class ThrottleRequests
{
    /**
     * The rate limiter instance.
     *
     * @var \Illuminate\Cache\RateLimiter
     */
    protected $limiter;

    /**
     * Create a new request throttler.
     *
     * @param  \Illuminate\Cache\RateLimiter $limiter
     */
    public function __construct(RateLimiter $limiter)
    {
        $this->limiter = $limiter;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  int $maxAttempts
     * @param  int $decayMinutes
     * @return mixed
     */
    public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        $key = $this->resolveRequestSignature($request);

        if ($this->limiter->tooManyAttempts($key, $maxAttempts, $decayMinutes)) {
            return $this->buildResponse($key, $maxAttempts);
        }

        $this->limiter->hit($key, $decayMinutes);

        $response = $next($request);

        return $this->addHeaders(
            $response, $maxAttempts,
            $this->calculateRemainingAttempts($key, $maxAttempts)
        );
    }

    /**
     * Resolve request signature.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function resolveRequestSignature($request)
    {
        return $request->fingerprint();
    }

    /**
     * Create a 'too many attempts' response.
     *
     * @param  string $key
     * @param  int $maxAttempts
     * @return \Illuminate\Http\Response
     */
    protected function buildResponse($key, $maxAttempts)
    {
        $message = json_encode([
            'error' => [
                'message' => 'Too many attempts, please slow down the request.' //may comes from lang file
            ],
            'status_code' => 4029 //your custom code
        ]);

        $response = new Response($message, 429);

        $retryAfter = $this->limiter->availableIn($key);

        return $this->addHeaders(
            $response, $maxAttempts,
            $this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
            $retryAfter
        );
    }

    /**
     * Add the limit header information to the given response.
     *
     * @param  \Symfony\Component\HttpFoundation\Response $response
     * @param  int $maxAttempts
     * @param  int $remainingAttempts
     * @param  int|null $retryAfter
     * @return \Illuminate\Http\Response
     */
    protected function addHeaders(Response $response, $maxAttempts, $remainingAttempts, $retryAfter = null)
    {
        $headers = [
            'X-RateLimit-Limit' => $maxAttempts,
            'X-RateLimit-Remaining' => $remainingAttempts,
        ];

        if (!is_null($retryAfter)) {
            $headers['Retry-After'] = $retryAfter;
            $headers['Content-Type'] = 'application/json';
        }

        $response->headers->add($headers);

        return $response;
    }

    /**
     * Calculate the number of remaining attempts.
     *
     * @param  string $key
     * @param  int $maxAttempts
     * @param  int|null $retryAfter
     * @return int
     */
    protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
    {
        if (!is_null($retryAfter)) {
            return 0;
        }

        return $this->limiter->retriesLeft($key, $maxAttempts);
    }
}

потомapp/Http/Kernel.phpВ файле:

'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,

Заменить:

'throttle' => \App\Http\Middleware\ThrottleRequests::class,

Готово.

Информационный магазин дроссельной заслонки

Наконец, позвольте мне сказать,ThrottleЭти частотные данные хранятся вcacheвнутренний,Laravelдефолтcache driverдаfileэтоthrottleИнформация хранится по умолчанию вcacheфайл, если вашcache driverЕсли заменить его на redis, то информация будет храниться в redis.Записываемая информация на самом деле очень простая.ThrottleПодпись объекта запроса (хэш метода HTTP-запроса, доменного имени, URI и IP-адреса клиента) будет использоваться в качестве ключа кэша для записи количества клиентских запросов.