Laravel Middleware: Поглиблений огляд автентифікації, Rate Limiting та власних middleware

Вичерпний посібник з middleware у Laravel — від автентифікації та rate limiting до створення власних класів middleware. Практичні приклади коду та патерни для продакшену.

Laravel Middleware Deep Dive

Middleware у Laravel виконує роль фільтрувального шару між вхідними HTTP-запитами та логікою застосунку. Кожен запит проходить через конвеєр класів middleware перш ніж потрапити до контролера, і кожна відповідь повертається тим самим шляхом. Розуміння цього механізму є критично важливим для побудови безпечних та продуктивних Laravel-застосунків у 2026 році.

Middleware коротко

Middleware перехоплює HTTP-запити до того, як вони досягнуть маршрутів. Laravel 12 реєструє всі middleware у файлі bootstrap/app.php за допомогою fluent API. Вбудовані middleware забезпечують автентифікацію, захист від CSRF, керування сесіями та rate limiting одразу після встановлення.

Як працює конвеєр middleware у Laravel

HTTP-ядро Laravel обробляє кожен запит через стек middleware. Кожен middleware отримує запит, виконує свою логіку, а потім передає запит до наступного шару через $next($request) або перериває конвеєр, повертаючи відповідь безпосередньо.

Ця архітектура реалізує патерн Chain of Responsibility (Ланцюг відповідальності). Middleware може діяти до того, як запит досягне контролера (наприклад, перевірка автентифікації), після генерації відповіді (наприклад, додавання заголовків) або в обох випадках одночасно.

app/Http/Middleware/LogRequestTime.phpphp
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;

class LogRequestTime
{
    public function handle(Request $request, Closure $next): Response
    {
        $start = microtime(true);          // Capture start time

        $response = $next($request);       // Pass to next middleware

        $duration = microtime(true) - $start;
        Log::info('Request completed', [
            'url'      => $request->url(),
            'method'   => $request->method(),
            'duration' => round($duration * 1000, 2) . 'ms',
        ]);

        return $response;                  // Return response up the stack
    }
}

Цей middleware обгортає запит: фіксує час початку до обробки та логує тривалість після повернення відповіді. Цей патерн до/після є центральним принципом роботи middleware.

Middleware автентифікації: Захист маршрутів

Laravel постачається з аліасом middleware auth, який відповідає Illuminate\Auth\Middleware\Authenticate. Застосування його до маршруту гарантує, що лише автентифіковані користувачі зможуть отримати доступ. Неавтентифіковані користувачі отримують відповідь 401 (API) або перенаправляються на сторінку входу (web).

routes/web.phpphp
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\ProfileController;

// Single route protection
Route::get('/dashboard', [DashboardController::class, 'index'])
    ->middleware('auth');

// Group protection for multiple routes
Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'show']);
    Route::put('/profile', [ProfileController::class, 'update']);
    Route::delete('/profile', [ProfileController::class, 'destroy']);
});

Автентифікація з кількома Guard

Застосунки з кількома типами користувачів (адмін-панель, клієнтська зона, API) використовують автентифікацію на основі guard. Middleware auth приймає параметр guard, який визначає, який драйвер автентифікації використовувати.

routes/api.phpphp
// API routes use the 'sanctum' guard
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', fn (Request $request) => $request->user());
    Route::apiResource('/orders', OrderController::class);
});

// routes/web.php
// Admin routes use a custom 'admin' guard
Route::middleware('auth:admin')->prefix('admin')->group(function () {
    Route::get('/dashboard', [AdminController::class, 'index']);
    Route::get('/users', [AdminController::class, 'users']);
});

Параметр guard після двокрапки вказує Laravel, проти якої конфігурації автентифікації виконувати перевірку. Це дозволяє зберігати логіку автентифікації чистою та розділеною між різними частинами застосунку.

Middleware Guest

Middleware guest є протилежністю auth — пропускає лише неавтентифікованих користувачів. Застосування його до маршрутів входу та реєстрації запобігає повторному доступу вже автентифікованих користувачів до цих сторінок.

Rate Limiting з Throttle Middleware

Rate limiting у Laravel захищає маршрути від зловживань за допомогою вбудованого middleware throttle. Найпростіша форма приймає два параметри: максимальну кількість запитів та часове вікно в хвилинах.

routes/api.phpphp
// Allow 60 requests per minute per user
Route::middleware('throttle:60,1')->group(function () {
    Route::get('/posts', [PostController::class, 'index']);
    Route::get('/posts/{post}', [PostController::class, 'show']);
});

// Stricter limit for write operations
Route::middleware(['auth:sanctum', 'throttle:10,1'])->group(function () {
    Route::post('/posts', [PostController::class, 'store']);
    Route::put('/posts/{post}', [PostController::class, 'update']);
});

Іменовані Rate Limiter для розширеного контролю

Визначення іменованих rate limiter у AppServiceProvider надає точний контроль над лімітами залежно від контексту користувача. Цей підхід значно гнучкіший за inline-параметри throttle.

app/Providers/AppServiceProvider.phpphp
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;

public function boot(): void
{
    // API rate limiter with tiered access
    RateLimiter::for('api', function (Request $request) {
        $user = $request->user();

        if ($user?->hasSubscription('enterprise')) {
            return Limit::perMinute(500)->by($user->id);   // Enterprise: 500/min
        }

        if ($user) {
            return Limit::perMinute(100)->by($user->id);   // Authenticated: 100/min
        }

        return Limit::perMinute(20)->by($request->ip());   // Anonymous: 20/min
    });

    // Login limiter to prevent brute force
    RateLimiter::for('login', function (Request $request) {
        return Limit::perMinute(5)
            ->by($request->ip())                            // Key by IP address
            ->response(function () {                        // Custom exceeded response
                return response()->json([
                    'message' => 'Too many login attempts. Try again in a minute.',
                ], 429);
            });
    });
}

Застосування іменованих limiter до маршрутів використовує синтаксис throttle:назва:

routes/api.phpphp
Route::middleware('throttle:api')->group(function () {
    Route::apiResource('/posts', PostController::class);
});

// routes/web.php
Route::middleware('throttle:login')
    ->post('/login', [AuthController::class, 'login']);

Багаторівневий rate limiter вище демонструє продакшен-патерн: enterprise-користувачі отримують вищі ліміти, автентифіковані користувачі — помірні, а анонімні запити обмежуються агресивно. Метод by() визначає ключ rate limit — ідентифікатор користувача для автентифікованих та IP-адресу як запасний варіант.

Готовий до співбесід з Laravel?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Створення власних middleware з нуля

Створення власних middleware охоплює сценарії, які вбудовані middleware не обробляють. Artisan-команда make:middleware генерує новий клас із правильною структурою.

bash
php artisan make:middleware EnsureUserHasRole

Middleware контролю доступу на основі ролей

Поширений патерн власного middleware забезпечує авторизацію на основі ролей на рівні маршруту, приймаючи назви ролей як параметри.

app/Http/Middleware/EnsureUserHasRole.phpphp
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserHasRole
{
    public function handle(Request $request, Closure $next, string ...$roles): Response
    {
        $user = $request->user();

        if (! $user || ! $user->hasAnyRole($roles)) {
            abort(403, 'Insufficient permissions.');
        }

        return $next($request);
    }
}

Варіативний параметр ...$roles дозволяє передавати кілька ролей, розділених комами. Реєстрація та використання виглядають наступним чином:

bootstrap/app.phpphp
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \App\Http\Middleware\EnsureUserHasRole::class,
    ]);
})

// routes/web.php
Route::middleware('role:admin')->group(function () {
    Route::get('/admin', [AdminController::class, 'index']);
});

// Multiple roles: admin OR editor can access
Route::middleware('role:admin,editor')->group(function () {
    Route::resource('/articles', ArticleController::class);
});

Middleware трансформації запитів

Middleware може модифікувати запит перш ніж він досягне контролера. Приклад middleware для JSON API, що перевіряє заголовки content type та обрізає рядкові дані:

app/Http/Middleware/ApiRequestSanitizer.phpphp
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class ApiRequestSanitizer
{
    public function handle(Request $request, Closure $next): Response
    {
        // Reject non-JSON requests on API routes
        if (! $request->expectsJson() && $request->isMethod('POST')) {
            return response()->json(
                ['error' => 'Content-Type must be application/json'],
                415
            );
        }

        // Trim all string inputs
        $input = $request->all();
        array_walk_recursive($input, function (&$value) {
            if (is_string($value)) {
                $value = trim($value);
            }
        });
        $request->merge($input);

        return $next($request);
    }
}

Цей middleware обробляє два аспекти: валідує тип контенту для POST-запитів та санітизує всі рядкові вхідні дані, видаляючи пробіли.

Реєстрація middleware у Laravel 12

Laravel 12 централізує всю реєстрацію middleware у файлі bootstrap/app.php. Це замінило старший підхід з файлом app/Http/Kernel.php, який існував до Laravel 11.

bootstrap/app.phpphp
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        // Global middleware (runs on every request)
        $middleware->append(
            \App\Http\Middleware\LogRequestTime::class
        );

        // Add to the 'web' middleware group
        $middleware->web(append: [
            \App\Http\Middleware\TrackPageViews::class,
        ]);

        // Add to the 'api' middleware group
        $middleware->api(prepend: [
            \App\Http\Middleware\ApiRequestSanitizer::class,
        ]);

        // Register aliases for route-level use
        $middleware->alias([
            'role'       => \App\Http\Middleware\EnsureUserHasRole::class,
            'subscribed' => \App\Http\Middleware\EnsureUserIsSubscribed::class,
        ]);

        // Control execution order
        $middleware->priority([
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\Auth\Middleware\Authenticate::class,
            \App\Http\Middleware\EnsureUserHasRole::class,
        ]);
    })
    ->create();

Масив priority має значення, коли до одного маршруту призначено кілька middleware. Laravel сортує їх відповідно до цього списку, гарантуючи запуск сесії перед автентифікацією, а автентифікації — перед перевіркою ролей.

Порядок виконання middleware

Middleware виконується в порядку реєстрації. Для middleware на рівні маршруту масив priority перевизначає порядок за замовчуванням. Автентифікацію слід завжди розміщувати перед авторизацією, щоб уникнути перевірки ролей для неавтентифікованих запитів.

Terminable Middleware для завдань після відповіді

Terminable middleware виконує логіку після відправлення відповіді клієнту. Це корисно для логування, аналітики або задач очищення, які не повинні блокувати користувача.

app/Http/Middleware/CollectAnalytics.phpphp
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response;

class CollectAnalytics
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);  // Pass through without delay
    }

    public function terminate(Request $request, Response $response): void
    {
        // Runs after response is sent to client
        DB::table('analytics')->insert([
            'path'        => $request->path(),
            'method'      => $request->method(),
            'status_code' => $response->getStatusCode(),
            'user_id'     => $request->user()?->id,
            'ip'          => $request->ip(),
            'created_at'  => now(),
        ]);
    }
}

Метод terminate отримує як оригінальний запит, так і фінальну відповідь. Middleware слід зареєструвати як singleton у AppServiceProvider, щоб гарантувати, що той самий екземпляр обробляє як handle(), так і terminate().

Практичні патерни middleware для продакшену

Кілька патернів middleware послідовно з'являються у продакшен Laravel-застосунках.

Обхід режиму обслуговування — дозволяє внутрішнім IP-адресам отримувати доступ до застосунку під час обслуговування:

app/Http/Middleware/MaintenanceBypass.phpphp
class MaintenanceBypass
{
    private array $allowedIps = ['192.168.1.0/24', '10.0.0.1'];

    public function handle(Request $request, Closure $next): Response
    {
        if (app()->isDownForMaintenance()) {
            foreach ($this->allowedIps as $ip) {
                if ($request->ip() === $ip) {
                    return $next($request);
                }
            }
        }

        return $next($request);
    }
}

Заголовки безпеки — додає HSTS, політику безпеки контенту та інші заголовки до кожної відповіді:

app/Http/Middleware/SecurityHeaders.phpphp
class SecurityHeaders
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->headers->set(
            'Strict-Transport-Security',
            'max-age=31536000; includeSubDomains'
        );

        return $response;
    }
}

Ці патерни демонструють дві основні позиції middleware: до запиту (обхід обслуговування перевіряє IP та потенційно блокує) та після відповіді (заголовки безпеки модифікують вихідну відповідь).

Готовий до співбесід з Laravel?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Висновок

  • Middleware у Laravel працює як конвеєр: кожен клас обробляє запит, виконує над ним дії та передає далі або перериває ланцюг, повертаючи відповідь
  • Middleware auth захищає маршрути за допомогою автентифікації на основі guard, підтримуючи кілька типів користувачів через синтаксис auth:guard
  • Rate limiting через middleware throttle та іменовані визначення RateLimiter::for() забезпечує багаторівневий контроль доступу на основі контексту користувача
  • Власні middleware обробляють наскрізні аспекти — перевірку ролей, санітизацію запитів, заголовки безпеки — без засмічення контролерів
  • Уся реєстрація middleware у Laravel 12 відбувається у bootstrap/app.php за допомогою fluent API, а масив priority контролює порядок виконання
  • Terminable middleware виконує задачі після відповіді (аналітика, логування) без впливу на затримку для користувача
  • Параметри middleware через синтаксис :param зберігають визначення маршрутів виразними, а класи middleware — придатними для повторного використання в різних контекстах

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

#laravel
#middleware
#php
#authentication
#rate-limiting

Поділитися

Пов'язані статті