Skip to content

5.16 Middleware en Laravel: Protegiendo APIs

5.16.1 Introducción

Un middleware en Laravel es una clase que actúa como un filtro entre la petición HTTP del cliente y el controlador que procesa esa petición. Su función principal es la de interceptar las peticiones y decidir si deben continuar o no hacia el siguiente paso del ciclo de vida de Laravel.

Los middlewares se utilizan para muchas tareas comunes:

  • Verificar si el usuario está autenticado.
  • Restringir el acceso según roles o permisos.
  • Aplicar límites de peticiones por segundo.
  • Registrar logs o realizar acciones antes o después de la petición.

¿Dónde se sitúa un middleware en el ciclo MVC?

graph TD
A[Petición HTTP] --> B[Middleware]
B --> C[Controlador]
C --> D[Modelo]
D --> E[Respuesta HTTP]

Como puedes ver, el middleware intercepta la petición antes de que llegue al controlador y también puede modificar la respuesta después.


5.16.2 Ubicación y estructura de los Middlewares

Los middlewares personalizados se encuentran en:

app/Http/Middleware

Laravel ya incluye muchos middlewares por defecto. Están registrados en app/Http/Kernel.php. Ahí se dividen en:

  • Middlewares globales: se aplican a todas las rutas.
  • Middlewares de grupo: se aplican a rutas web o api según corresponda.
  • Middlewares individuales: se pueden asignar directamente a una ruta.

Algunos ejemplos comunes:

Middleware Descripción
auth Verifica si el usuario está autenticado
guest Redirige si el usuario ya está autenticado
throttle Controla el número de peticiones por minuto
verified Comprueba si el email está verificado
signed Valida URLs firmadas

5.16.3 Crear un Middleware personalizado

Mirar que la base de datos esté creada y vacía para que la migración inicial funcione sin problemas.

Vamos a crear un middleware simple que permita o no acceder a una ruta según el valor de una cabecera personalizada. Vamos a controlar una ruta de API. Por tanto lo primero será instalar el soporte de API:

php artisan install:api

Al final de la instalación nos pedirá que hagamos la miración le indicamos que sí o la hacemos manualmente al terminar la instalación.

php artisan migrate

Paso 1: Crear la clase middleware

php artisan make:middleware EjemploMiddleware

Esto crea un archivo en app/Http/Middleware/EjemploMiddleware.php. Esta va a ser un middleware sencillo, únicamente nos redirigirá a otra ruta.

Paso 2: Editar el middleware

Ejemplo de validación de cabecera HTTP

1
2
3
4
5
public function handle($request, Closure $next)
{
    return redirect()->route('no-access.index');
    //return $next($request);
}

Paso 3: Registrar el middleware

En laravel 12 el registro de las rutas se hace en bootstrap/app.php. Entramos en el fichero y añadimos el middleware a la propiedad $routeMiddleware:

bootstrap/app.php
1
2
3
4
->withMiddleware(function (Middleware $middleware) {
        // Register middleware
        $middleware->append(EjemploMiddleware::class);
    })

Ahora si queremos dar un alias al middleware para usarlo más fácilmente:

bootstrap/app.php
1
2
3
4
5
->withMiddleware(function (Middleware $middleware) {
        // Register middleware
        $middleware->alias([
                'ejemplo' => EjemploMiddleware::class
            ]);

Paso 4: Usar en las rutas

Creamos el controlador para manejar las rutas protegidas por el middleware:

php artisan make:controller SecretController

Controlador de ejemplo para manejar rutas protegidas por middleware

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;

class SecretController extends Controller
{
    public function index()
    {
        return response()->json([
            'status' => 'true',
            'message' => 'Acceso permitido'
        ], 200);
    }

    public function noAccess()
    {
        return response()->json([
            'status' => 'false',
            'message' => 'Acceso denegado'
        ], 403);
    }
}

Ahora vamos a definir las rutas que usaremos para probar el middleware. Vamos a crear dos rutas, una protegida por el middleware y otra a la que redirigiremos si no se cumple la condición del middleware.

Vamos a crear dos rutas:

  • /secret: protegida por el middleware. No se puede acceder.
  • /no-access: ruta a la que redirigimos si no se cumple la condición del middleware.

En routes/api.php:

Route::middleware('ejemplo')
    ->get('/secret', [SecretController::class, 'index'])
    ->name('secret.index');

Route::get('/no-access', [SecretController::class, 'noAccess'])
    ->name('no-access.index');
En este caso como el middleware no tiene condiciones, siempre redirigirá a la ruta /no-access. Si intentamos acceder a /secret:

http://localhost:8000/api/secret
Nos redirigirá a:

http://localhost:8000/api/no-access

Y nos devolverá error 403 y el JSON:

{
    "status" : "false",
    "message":"Acceso denegado"
}

Puedes probar a quirtar el middleware y verás que ahora sí puedes acceder a la ruta /secret:

http://localhost:8000/api/secret
{
    "status" : "true",
    "message":"Acceso permitido"
}

Paso 5: Aplicar el middleware a un grupo de rutas

Si quieres aplicar el middleware a un grupo de rutas, puedes hacerlo así:

Route::middleware('ejemplo')->group(function () {
    Route::get('/secret', [SecretController::class, 'index'])
    ->name('secret.index');
    Route::get('/secret2', [SecretController::class, 'index'])
        ->name('secret2.index');
        Route::get('/no-access', [SecretController::class, 'noAccess'])
        ->name('no-access.index')->withoutMiddleware('ejemplo');
});

Podemos ver como agrupamos las rutas afectadas por el middleware. En caso de que no queramos aplicar el middleware a una ruta concreta, podemos usar el método withoutMiddleware para excluirla.

Si quermos aplicar más de un middleware a un grupo de rutas, podemos hacerlo así:

Route::middleware(['ejemplo', 'auth:sanctum'])->group(function () {
    Route::get('/secret', [SecretController::class, 'index'])
        ->name('secret.index');
    Route::get('/secret2', [SecretController::class, 'index'])
        ->name('secret2.index');
    Route::get('/no-access', [SecretController::class, 'noAccess'])
        ->name('no-access.index')->withoutMiddleware('ejemplo');
});
En este caso, el middleware auth:sanctum se aplicará a todas las rutas del grupo, y el middleware ejemplo se aplicará solo a las rutas /secret y /secret2.


5.16.4 Crear rutas API de autenticación

Vamos a crear una API de autenticación básica para registrarse, iniciar sesión. Utilizaremos tokens para identificar al usuario.

Para ello, Laravel recomienda usar Sanctum, que simplifica mucho la generación de tokens:

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

En config/sanctum.php, puedes ajustar la duración del token, pero lo dejamos por defecto.

Configurar el modelo User

En app/Models/User.php, asegurarse de usar el trait:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
    ...
    ...
}

Paso 1: Crear un controlador de autenticación

Vamos a crear un controlador para manejar la autenticación. Puedes usar el comando:

php artisan make:controller AuthController
class AuthController extends Controller
{
    public function createUser(UserRequest $request)
    {

    }

    public function loginUser(Request $request)
    {

    }

}

Vamos a crear también un UserRequest para validar los datos de entrada:

php artisan make:request UserRequest
class UserRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string'
        ];
    }
}

también vamos a crear un LoginUserRequest para validar los datos de entrada:

php artisan make:request LoginUserRequest
class LoginUserRequest extends FormRequest
{
    public function rules()
    {
        return [
            'email' => 'required|string|email|max:255',
            'password' => 'required|string'
        ];
    }
}

Ahora que temos las dos clases de request, vamos a usarlas en el controlador:

use Illuminate\Support\Facades\Auth;
use App\Http\Requests\LoginUserRequest;
use App\Http\Requests\UserRequest;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller
{
    public function createUser(UserRequest $request)
    {
        $user = User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => Hash::make($request->password),
             ]);

        return response()->json([
            'status' => 'true',
            'message' => 'Usuario creado correctamente',
            'token' => $user->createToken('api-token')->plainTextToken,
        ], 200);
    }

    public function loginUser(LoginUserRequest $request)
    {
        if (!Auth::attempt($request->only(['email', 'password']))) {
            return response()->json([
                'status' => 'false',
                'message' => 'Credenciales incorrectas',
            ], 401);
        }

        $user = User::where('email', $request->email)->first();
        return response()->json([
            'status' => 'true',
            'message' => 'Usuario logueado correctamente',
            'token' => $user->createToken('api-token')->plainTextToken,
        ], 200);
    }

}

Paso 2: Registro de usuario

Ahora que tenemos el diseño del controlador, vamos a crear las rutas para el registro y el login. Vamos a crear las rutas en routes/api.php:

Route::post('/register', [AuthController::class, 'createUser'])
    ->name('register');
Route::post('/login', [AuthController::class, 'loginUser'])
    ->name('login');
Ahora añadimos una tercera ruta a la que sólo se prodrá acceder si el usuario está autenticado:

Route::middleware('auth:sanctum')
    ->get('/user', function (Request $request) {
        return $request->user();
    })->name('user');

Para probar la API, puedes usar Postman o cualquier cliente REST.

Paso 3: Probar la API

Registro de usuario

Para registrar un usuario, envía una petición POST a la ruta /api/register con el siguiente cuerpo:

{
    "name": "Juan",
    "email": "juan@example.com",
    "password": "123456"
}
Si el registro es exitoso, recibirás una respuesta JSON con el token de acceso:

{
    "status": "true",
    "message": "Usuario creado correctamente",
    "token": "cadf3e... (tu token)"
}

Login de usuario

Para iniciar sesión, envía una petición POST a la ruta /api/login con el siguiente cuerpo:

{
    "email": "juan@example.com",
    "password": "123456"
}
Si el login es exitoso, recibirás una respuesta JSON con el token de acceso:

{
    "status": "true",
    "message": "Usuario logueado correctamente",    
    "token": "cadf3e... (tu token)"
}

5.16.5 Ruta protegida por token con auth:sanctum

Ahora nos fijamos en la última ruta creada y que hemos protegido con el middleware auth:sanctum.

Route::->middleware('auth:sanctum')->get('/user', function (Request $request) {
    return response()->json($request->user());
});

Esta ruta devuelve el usuario autenticado.

Para acceder:

  • Hacer login y copiar el token.
  • Enviar petición con la cabecera:
Authorization: Bearer TU_TOKEN_AQUI

Si no se envía el token o es inválido, devuelve 401.


5.16.6 Consejos para probar desde Postman o REST Client

En Postman:

  • Cabecera Accept: application/json
  • Cabecera Authorization: Bearer {token}

En REST Client (VS Code):

GET http://localhost:8080/api/test

###
POST http://localhost:8080/api/create HTTP/1.1
Content-Type: application/json

{
  "name": "Test",
  "email": "test@example.com",
  "password": "password"
}

###
POST http://localhost:8080/api/login HTTP/1.1
Content-Type: application/json

{
  "email": "test@example.com",
  "password": "password"
}

###
POST http://localhost:8080/api/login HTTP/1.1
Content-Type: application/json

{
  "email": "test@example.com",
  "password": "bad_password"
}
###
GET http://localhost:8080/api/user HTTP/1.1
Authorization: Bearer 3|wKvMo8... (token)
Content-Type: application/json

5.16.7 Conclusiones

  • Los middlewares permiten controlar el acceso y comportamiento de las peticiones.
  • Pueden ser globales, por grupo o asignados a rutas.
  • Laravel permite crear middlewares personalizados con facilidad.
  • Para APIs, Laravel Sanctum es una forma segura y sencilla de implementar autenticación por token.
  • Toda esta lógica la veremos más adelante también aplicada a rutas web, roles y protección por permisos.

Próximo tema: middleware aplicado a vistas web y roles de usuario.