Tabla de contenido
- ¿Qué es un middleware?
- Middleware incluidos en Laravel
- Crear un middleware personalizado
- Registrar y aplicar el middleware
- Middleware de autenticación
- Middleware de roles y permisos
- Middleware de rate limiting
Imagina que cada petición HTTP que llega a tu API pasa por una serie de puertas antes de llegar al controlador. Cada puerta puede revisar la petición, modificarla, rechazarla o simplemente dejarla pasar. En Laravel, esas puertas se llaman middleware.
¿Qué es un middleware?
Un middleware es una capa de código que intercepta las peticiones HTTP antes de que lleguen al controlador (y también puede procesar las respuestas antes de enviarlas al cliente).
Petición → [Middleware 1] → [Middleware 2] → Controlador → Respuesta
Los middleware son perfectos para:
- Verificar que el usuario está autenticado
- Comprobar que el usuario tiene los permisos necesarios
- Limitar la cantidad de peticiones (rate limiting)
- Registrar logs de actividad
- Añadir headers de seguridad (CORS, etc.)
- Validar tokens de API
Middleware incluidos en Laravel
Laravel viene con varios middleware listos para usar:
// Algunos de los middleware predefinidos en Laravel:
'auth' // Verifica que el usuario está autenticado
'guest' // Solo permite acceso a usuarios no autenticados
'verified' // Requiere email verificado
'throttle:60,1' // Limita a 60 peticiones por minuto
'cors' // Gestiona las cabeceras CORS
Se aplican en las rutas o grupos de rutas directamente.
Crear un middleware personalizado
Genera el middleware con Artisan:
php artisan make:middleware VerificarTokenAPI
Esto crea el archivo en app/Http/Middleware/VerificarTokenAPI.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerificarTokenAPI {
public function handle(Request $request, Closure $next) {
// Lógica ANTES de llegar al controlador
$token = $request->header('X-API-Token');
if (!$token || $token !== config('app.api_token')) {
return response()->json([
'error' => 'Token de API inválido o faltante'
], 401);
}
// Si pasa la verificación, continúa al siguiente middleware o controlador
$response = $next($request);
// Lógica DESPUÉS del controlador (opcional)
$response->headers->set('X-Processed-By', '8devmx-api');
return $response;
}
}
El método handle recibe la petición y una función $next que continúa hacia el siguiente middleware o controlador. Si llamas a $next($request), la petición pasa. Si retornas una respuesta directamente (como el response()->json(...) del ejemplo), la petición se detiene ahí.
Registrar y aplicar el middleware
Registrar en app/Http/Kernel.php:
// Como middleware de ruta (se aplica explícitamente)
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'verificar.token' => \App\Http\Middleware\VerificarTokenAPI::class,
'check.role' => \App\Http\Middleware\CheckRole::class,
];
Aplicar en rutas individuales:
// routes/api.php
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware('auth');
Route::post('/articulos', [ArticuloController::class, 'store'])
->middleware(['auth', 'verificar.token']);
Aplicar a grupos de rutas:
Route::middleware(['auth', 'verificar.token'])->group(function () {
Route::get('/usuarios', [UsuarioController::class, 'index']);
Route::post('/usuarios', [UsuarioController::class, 'store']);
Route::delete('/usuarios/{id}', [UsuarioController::class, 'destroy']);
});
Aplicar en el constructor del controlador:
class UsuarioController extends Controller {
public function __construct() {
$this->middleware('auth');
$this->middleware('check.role:admin')->only(['destroy']);
$this->middleware('throttle:30,1')->only(['store', 'update']);
}
}
Middleware de autenticación
Un middleware de autenticación con JWT manual:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
class AutenticarJWT {
public function handle(Request $request, Closure $next) {
try {
$usuario = JWTAuth::parseToken()->authenticate();
if (!$usuario) {
return response()->json(['error' => 'Usuario no encontrado'], 404);
}
} catch (TokenExpiredException $e) {
return response()->json(['error' => 'Token expirado'], 401);
} catch (TokenInvalidException $e) {
return response()->json(['error' => 'Token inválido'], 401);
} catch (\Exception $e) {
return response()->json(['error' => 'Token no proporcionado'], 401);
}
return $next($request);
}
}
Middleware de roles y permisos
Para controlar el acceso según el rol del usuario:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CheckRole {
public function handle(Request $request, Closure $next, ...$roles) {
$usuario = $request->user();
if (!$usuario) {
return response()->json(['error' => 'No autenticado'], 401);
}
// $roles viene del middleware: 'check.role:admin,editor'
if (!in_array($usuario->rol, $roles)) {
return response()->json([
'error' => 'No tienes permisos para realizar esta acción'
], 403);
}
return $next($request);
}
}
Uso en rutas:
// Solo admins
Route::delete('/usuarios/{id}', [UsuarioController::class, 'destroy'])
->middleware('check.role:admin');
// Admins o editores
Route::post('/articulos', [ArticuloController::class, 'store'])
->middleware('check.role:admin,editor');
Middleware de rate limiting
Laravel incluye throttle por defecto, pero puedes crear uno personalizado:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\RateLimiter;
class LimitarPeticiones {
public function handle(Request $request, Closure $next, int $maxPeticiones = 60) {
$clave = 'rate-limit:' . $request->ip();
if (RateLimiter::tooManyAttempts($clave, $maxPeticiones)) {
$segundos = RateLimiter::availableIn($clave);
return response()->json([
'error' => 'Demasiadas peticiones',
'reintentar_en' => "$segundos segundos"
], 429);
}
RateLimiter::hit($clave, 60); // expira en 60 segundos
$response = $next($request);
return $response->withHeaders([
'X-RateLimit-Limit' => $maxPeticiones,
'X-RateLimit-Remaining' => RateLimiter::remaining($clave, $maxPeticiones),
]);
}
}
El middleware es uno de los patrones más elegantes de Laravel. Una vez que lo dominas, organizar la lógica de autorización, autenticación y validación de tu API se vuelve mucho más limpio y mantenible que mezclar todo esa lógica dentro de los controladores.