В этой статье я покажу реализацию промежуточного программного обеспечения, которое контролирует количество запросов на маршруте или группе маршрутов.
Я поясняю, что эта статья имеет образовательные цели, и она не должна быть единственным способом контроля доступа к api.
Представление сценария
Контроль количества запросов, которые получает ваше приложение, является интересной стратегией, поскольку позволяет избежать излишеств со стороны клиентских приложений.
Представляя себе сценарий, в котором наше приложение имеет обновленную информацию об эмиссии официальных документов, оно будет получать запросы от многих приложений одновременно с целью обновления собственных систем.
Это очень распространенный сценарий.
Бывает, что, учитывая большой объем информации, которую необходимо обработать, нашему приложению потребуется некоторое время, чтобы ответить на каждый запрос.
Среди приложений, обращающихся к нашей службе, есть одно, которое делает запросы каждые 5 секунд.
Если произвести быстрый подсчет, то только это приложение будет делать 12 запросов в минуту, 720 в час и 17 тысяч в день.
Чтобы избежать избытка и не навредить другим приложениям, мы введем блок для приложений, которые делают более 5 запросов в минуту.
Необходимые инструменты
- PHP 8 или выше
- Composer версии 2.0 или выше
- Текстовый редактор
Существуют программы установки, в которые встроены PHP, MySQL и Apache (веб-сервер), такие как WAMPSERVER, XAMPP и Laragon. Я оставляю на ваше усмотрение выбор одного из этих пакетов или другого, который вам знаком.
В качестве текстового редактора я использую Visual Studio Code. Кроме того, что он бесплатный, он позволяет устанавливать несколько расширений.
Проект
Проект, который необходимо разработать, представляет собой приложение Laravel, известное как фреймворк для веб-разработки, и на момент написания этой статьи находится в версии 9.
Войдя в терминал по своему выбору, введите следующую команду:
composer create-project --prefer-dist laravel/laravel ratelimit
Время, необходимое для выполнения этого шага, может варьироваться в зависимости от скорости подключения к Интернету и конфигурации компьютера.
После завершения этого шага нам нужно получить доступ к директории проекта.
Middleware
Внутри каталога проекта мы создадим промежуточное ПО с помощью следующей команды:
php artisan make:middleware CustomRateLimit
По умолчанию в приложениях Laravel промежуточное ПО, созданное с помощью этой команды, будет сохранено в AppHttpMiddleware.
Используя текстовый редактор по вашему выбору, напишем метод handle()
следующим образом:
<?php
namespace AppHttpMiddleware;
use Closure;
use IlluminateHttpRequest;
use IlluminateSupportFacadesRateLimiter;
class CustomRateLimit
{
/**
* Handle an incoming request.
*
* @param IlluminateHttpRequest $request
* @param Closure(IlluminateHttpRequest): (IlluminateHttpResponse|IlluminateHttpRedirectResponse) $next
* @return IlluminateHttpResponse|IlluminateHttpRedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$executed = RateLimiter::attempt(
'education'.$request->ip(),
$perMinute = 5,
function() {
}
);
if (!$executed) {
return response(
[
'message' => 'Too many attempts.. try again in '.RateLimiter::availableIn('education').' seconds'
], 429);
}
return $next($request);
}
}
Поясняя код, мы создаем ограничитель запросов через конструктор attempt
, который имеет 3 обязательных параметра и 1 необязательный параметр.
Первый параметр получает имя ограничителя.
Это имя должно быть уникальным, и чтобы удовлетворить эту потребность, мы объединим текст ‘education’ с ip, полученным через переменную $request.
ПРИМЕЧАНИЕ: класс IlluminateHttpRequest инкапсулирует всю информацию http-запроса в течение его жизненного цикла.
Во втором параметре конструктора attempt
мы сообщаем лимит запросов в минуту.
Третий параметр получает функцию обратного вызова. Реализация этой функции не является обязательной для валидации запросов. В этом случае мы сообщаем только заголовок метода, без реализации.
Последний параметр является необязательным. В нем мы изменяем интервал, в секундах, для проверки входящих запросов.
Например, если мы хотим блокировать на 5 минут, значение, которое нужно ввести в этот параметр, будет равно 300, или 60 секунд умножить на 5 минут.
Контроллер
Далее создадим контроллер.
php artisan make:controller RateLimitController
По умолчанию контроллеры, созданные с помощью вышеуказанной команды, сохраняются в AppHttpControllers.
Редактируя контроллер, мы реализуем метод для сброса счетчика запросов, разблокируя доступ к клиентскому приложению и возвращая пользовательское сообщение.
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use IlluminateSupportFacadesRateLimiter;
class RateLimitController extends Controller
{
public function clearLimit(Request $request)
{
RateLimiter::clear('education'.$request->ip());
return response(['message' => 'Attempts cleared for '.$request->ip()], 200);
}
}
Поясняя код, мы делаем вызов метода clear()
, который принимает в качестве параметра имя ограничителя.
Обратите внимание, что имя совпадает с именем, которое дано в реализации метода middleware handle(Request $request, Closure $next)
.
Маршрут
Теперь откроем файл маршрута, добавив два новых маршрута.
<?php
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
use AppHttpMiddlewareCustomRateLimit;
use AppHttpControllersRateLimitController;
Route::middleware([CustomRateLimit::class])->get('/rate-limit', function() {
$date = new DateTime();
$date->setTimezone(new DateTimeZone('-0300'));
return $date->format('Y-m-d H:i:s');
});
Route::post('/rate-limit/clear', [RateLimitController::class, 'clearLimit']);
Первый путь — это увеличение количества запросов, чтобы достичь предела, который мы установили в промежуточном ПО.
Как только лимит будет достигнут, мы выведем пользовательское сообщение, информирующее о том, сколько времени (в секундах) осталось у клиентского приложения для выполнения новых запросов.
Второй маршрут направлен на обнуление счетчика путем вызова метода clearLimit() контроллера RateLimitController.
Тестирование
Снова заходим в терминал по вашему выбору, в папке проекта запускаем сервер на порту 8108.
Совет: внутри VSCode при использовании комбинации клавиш Ctrl+’ в нижней части IDE откроется терминал, уже в папке проекта.
php artisan serve --port=8108
Используя расширение Thunder для VSCode, мы будем выполнять запросы по первому маршруту, пока не будет достигнут лимит повторных попыток.
А затем мы сделаем запрос к маршруту, который сбросит счетчик повторных попыток.
Примечания
По умолчанию в приложениях Laravel кэш предварительно настроен на файл.
Однако можно изменить конфигурацию для использования с базами данных, Memcached или Redis, изменив переключатель 'default'
в параметре
Класс ConfigCache.php.
Заключение
Целью этой статьи было показать, как реализовать способ избежать перегрузки при доступе к вашему приложению, временно блокируя некоторые приложения без остановки службы или нанесения вреда другим приложениям.
Я подчеркиваю, что этот подход не следует рассматривать как единственный вариант безопасности для вашего приложения.
Спасибо, что читаете, и до скорой встречи.
Используемые расширения
В веб-разработке хороший инструмент для тестирования api-запросов имеет большое значение.
Поэтому я оставляю здесь ссылку на расширение для VSCode Thunder RESTClient.
Расширение является бесплатным и очень надежным.
Для приложений Laravel расширение Laravel Extra Intellisense очень помогает в автоматическом включении ссылок.
Общественное хранилище
Проект опубликован на github в этом репозитории.