Módulo 4 · Lección 4 20 min

Middlewares — proteger rutas

Descubre cómo usar middlewares en Laravel para proteger rutas, verificar permisos y ejecutar lógica antes o después de las peticiones HTTP.

¿Qué es un Middleware?

Imagina un portero en la entrada de una discoteca. Antes de que cualquiera entre, el portero verifica que cumpla los requisitos: mayor de edad, sin prohibición de entrada, vestimenta adecuada. Si pasa todos los filtros, entra. Si no, se le niega el acceso.

Un middleware en Laravel es exactamente eso: un filtro que se ejecuta antes (o después) de que una petición HTTP llegue a tu controlador. Puedes usarlo para verificar si el usuario está autenticado, si tiene los permisos correctos, si el servidor acepta cierto tipo de peticiones, o para cualquier otra lógica transversal que afecte a múltiples rutas.

Middlewares Incluidos en Laravel

Laravel viene con varios middlewares listos para usar:

  • auth: Verifica que el usuario esté autenticado.
  • guest: Verifica que el usuario NO esté autenticado (para páginas de login).
  • verified: Verifica que el email del usuario esté verificado.
  • throttle: Limita el número de peticiones (rate limiting).
  • csrf: Verifica el token CSRF en formularios POST.

Aplicar Middleware a una Ruta

La forma más directa es encadenar ->middleware() a una ruta:

use App\Http\Controllers\DashboardController;

// Solo usuarios autenticados pueden acceder al dashboard
Route::get('/dashboard', [DashboardController::class, 'index'])
    ->middleware('auth')
    ->name('dashboard');

// Solo usuarios NO autenticados pueden ver el login
Route::get('/login', [LoginController::class, 'index'])
    ->middleware('guest')
    ->name('login');

Aplicar Middleware a un Grupo de Rutas

Lo más habitual es proteger grupos completos:

Route::middleware(['auth'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
    Route::get('/perfil', [PerfilController::class, 'index'])->name('perfil');
    Route::resource('productos', ProductoController::class);
});

Puedes combinar prefijos, nombres y middlewares:

Route::prefix('admin')
    ->name('admin.')
    ->middleware(['auth', 'verified'])
    ->group(function () {
        Route::get('/dashboard', [AdminController::class, 'dashboard'])->name('dashboard');
        Route::resource('usuarios', UsuarioController::class);
    });

Crear un Middleware Personalizado

Los middlewares de Laravel son geniales, pero a veces necesitas lógica personalizada. Por ejemplo, verificar que el usuario tenga rol de administrador:

php artisan make:middleware EsAdministrador

Esto crea app/Http/Middleware/EsAdministrador.php:

<?php

namespace App\Http\Middleware;

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

class EsAdministrador
{
    public function handle(Request $request, Closure $next): Response
    {
        // Verificar si el usuario está autenticado y es admin
        if (!$request->user() || !$request->user()->es_admin) {
            // Redirigir con un mensaje de error
            return redirect('/')->with('error', 'No tienes permisos para acceder a esta sección.');
        }

        // Si pasa la verificación, continuar con la petición
        return $next($request);
    }
}

La línea clave es return $next($request): esto pasa la petición al siguiente middleware o al controlador. Si no llamas a $next, la petición queda bloqueada aquí.

Registrar el Middleware

En Laravel 11, puedes registrar el middleware directamente en las rutas usando la clase completa:

use App\Http\Middleware\EsAdministrador;

Route::prefix('admin')
    ->middleware([EsAdministrador::class])
    ->group(function () {
        Route::get('/panel', [AdminController::class, 'panel']);
    });

O puedes darle un alias en bootstrap/app.php:

// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'admin' => \App\Http\Middleware\EsAdministrador::class,
        ]);
    })
    ->create();

Ahora puedes usar el alias en las rutas:

Route::middleware(['auth', 'admin'])->group(function () {
    Route::get('/admin/panel', [AdminController::class, 'panel']);
});

Middleware con Parámetros

Los middlewares pueden aceptar parámetros adicionales. Esto es útil para verificar roles específicos:

<?php

namespace App\Http\Middleware;

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

class TieneRol
{
    public function handle(Request $request, Closure $next, string $rol): Response
    {
        if (!$request->user() || $request->user()->rol !== $rol) {
            abort(403, 'Acceso denegado.');
        }

        return $next($request);
    }
}

En las rutas, pasas el parámetro con dos puntos:

Route::get('/admin/usuarios', [UsuarioController::class, 'index'])
    ->middleware('rol:administrador');

Route::get('/moderacion', [ModeracionController::class, 'index'])
    ->middleware('rol:moderador');

// Múltiples roles aceptados
Route::get('/reportes', [ReporteController::class, 'index'])
    ->middleware('rol:administrador,supervisor');

Middleware After: Ejecutar Lógica Después del Controlador

La mayoría de middlewares ejecutan lógica ANTES del controlador (before middleware). Pero también puedes ejecutar lógica DESPUÉS de que el controlador haya generado la respuesta:

<?php

namespace App\Http\Middleware;

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

class RegistrarPeticion
{
    public function handle(Request $request, Closure $next): Response
    {
        // Código ANTES del controlador
        $inicio = microtime(true);

        // Procesar la petición
        $response = $next($request);

        // Código DESPUÉS del controlador
        $tiempoMs = round((microtime(true) - $inicio) * 1000, 2);
        Log::info("Petición {$request->method()} {$request->path()} - {$tiempoMs}ms");

        return $response;
    }
}

Este middleware registra en los logs el tiempo que tardó cada petición.

Rate Limiting con Throttle

El middleware throttle limita cuántas peticiones puede hacer un usuario en un período de tiempo. Muy útil para proteger endpoints sensibles:

// Máximo 60 peticiones por minuto (por defecto)
Route::middleware('throttle:60,1')->group(function () {
    Route::get('/api/datos', [ApiController::class, 'datos']);
});

// 5 intentos de login por minuto
Route::post('/login', [LoginController::class, 'store'])
    ->middleware('throttle:5,1')
    ->name('login');

El formato es throttle:{intentos},{minutos}.

Ver los Middlewares Activos

Para ver qué middlewares están aplicados a cada ruta:

php artisan route:list --verbose

La columna “Middleware” mostrará todos los middlewares activos para cada ruta.

Deshabilitar Middleware en Testing

Durante las pruebas, a veces quieres deshabilitar middlewares para simplificar los tests:

// En un test
public function test_el_dashboard_es_accesible(): void
{
    $usuario = User::factory()->create();

    $response = $this->actingAs($usuario)->get('/dashboard');

    $response->assertStatus(200);
}

Usando actingAs(), le dices a Laravel que actúe como si ese usuario estuviera autenticado, así el middleware auth lo dejará pasar.

Resumen

Los middlewares son la herramienta perfecta para aplicar lógica transversal a múltiples rutas de forma DRY (Don’t Repeat Yourself). En lugar de repetir la misma verificación en cada controlador, la centralizas en un middleware y la aplicas a las rutas que lo necesiten. El middleware auth es el más usado para proteger secciones privadas, pero crear tus propios middlewares personalizados te da un control total sobre quién puede acceder a cada parte de tu aplicación.

Quiz

Pon a prueba lo aprendido

Responde las preguntas para comprobar que has entendido los conceptos clave.

1. ¿Qué hace el middleware "auth" en Laravel?

2. ¿Cuál es el comando Artisan para crear un middleware personalizado?

3. ¿Qué línea de código ejecuta el siguiente middleware en la cadena de procesamiento?