Lección 30 de 45 12 min de lectura

Proteger rutas

Una vez que tienes autenticación en tu aplicación, necesitas restringir el acceso a ciertas páginas. Laravel usa middleware para proteger rutas, permitiendo que solo usuarios autenticados accedan a contenido privado.

El middleware auth

El middleware auth es el encargado de verificar si un usuario está autenticado. Si no lo está, lo redirige automáticamente a la página de login.

Este middleware viene incluido en Laravel y está registrado en bootstrap/app.php. No necesitas crearlo ni configurarlo manualmente.

Proteger una ruta individual

La forma más simple de proteger una ruta es encadenar el método middleware():

php
<?php

use Illuminate\Support\Facades\Route;

// Ruta pública - cualquiera puede acceder
Route::get('/', function () {
    return view('welcome');
});

// Ruta protegida - solo usuarios autenticados
Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware('auth');

Si un usuario no autenticado intenta acceder a /dashboard, Laravel lo redirige a /login automáticamente.

Proteger múltiples rutas con grupos

Cuando tienes varias rutas que requieren autenticación, es más limpio agruparlas:

php
<?php

use App\Http\Controllers\ProfileController;
use App\Http\Controllers\SettingsController;
use Illuminate\Support\Facades\Route;

// Rutas públicas
Route::get('/', function () {
    return view('welcome');
});

// Rutas protegidas
Route::middleware('auth')->group(function () {
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');

    Route::get('/profile', [ProfileController::class, 'edit'])
        ->name('profile.edit');

    Route::patch('/profile', [ProfileController::class, 'update'])
        ->name('profile.update');

    Route::get('/settings', [SettingsController::class, 'index'])
        ->name('settings');
});

Todas las rutas dentro del grupo heredan el middleware auth. Esto mantiene tu código organizado y evita repetir ->middleware('auth') en cada ruta.

Combinar middleware auth con prefijos

Puedes combinar el middleware con prefijos de URL para organizar mejor tus rutas:

php
<?php

use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\UserController;
use Illuminate\Support\Facades\Route;

// Panel de administración
Route::middleware('auth')
    ->prefix('admin')
    ->name('admin.')
    ->group(function () {
        // /admin/dashboard -> admin.dashboard
        Route::get('/dashboard', [DashboardController::class, 'index'])
            ->name('dashboard');

        // /admin/users -> admin.users.index
        Route::get('/users', [UserController::class, 'index'])
            ->name('users.index');

        // /admin/users/{user} -> admin.users.show
        Route::get('/users/{user}', [UserController::class, 'show'])
            ->name('users.show');
    });

El middleware verified

Además de auth, Laravel incluye el middleware verified que verifica que el usuario haya confirmado su email. Puedes combinar ambos:

php
<?php

// Solo usuarios autenticados Y con email verificado
Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Si el usuario no ha verificado su email, será redirigido a una página que le pide verificarlo.

Recuerda: Para que verified funcione, tu modelo User debe implementar la interfaz MustVerifyEmail (visto en la lección anterior).

Middleware en controladores con resource

Cuando usas rutas de tipo resource, puedes aplicar middleware directamente al registrarlas. Por ejemplo, si quieres que index y show sean públicos pero el resto requiera autenticación:

php
<?php

use App\Http\Controllers\ArticleController;
use Illuminate\Support\Facades\Route;

// Rutas públicas del resource
Route::resource('articles', ArticleController::class)
    ->only(['index', 'show']);

// Rutas protegidas del resource
Route::resource('articles', ArticleController::class)
    ->except(['index', 'show'])
    ->middleware('auth');

También puedes separar las rutas en grupos para mayor claridad:

php
<?php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

// Cualquiera puede ver posts
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');

// Solo usuarios autenticados pueden crear/editar/eliminar
Route::middleware('auth')->group(function () {
    Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
    Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
    Route::get('/posts/{post}/edit', [PostController::class, 'edit'])->name('posts.edit');
    Route::put('/posts/{post}', [PostController::class, 'update'])->name('posts.update');
    Route::delete('/posts/{post}', [PostController::class, 'destroy'])->name('posts.destroy');
});

El middleware guest

El middleware guest es lo opuesto a auth: solo permite acceso a usuarios no autenticados. Es útil para páginas como login o registro, donde no tiene sentido que un usuario ya autenticado las vea:

php
<?php

use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\Auth\RegisterController;

// Solo para usuarios no autenticados
Route::middleware('guest')->group(function () {
    Route::get('/login', [LoginController::class, 'create'])
        ->name('login');

    Route::post('/login', [LoginController::class, 'store']);

    Route::get('/register', [RegisterController::class, 'create'])
        ->name('register');

    Route::post('/register', [RegisterController::class, 'store']);
});

Si un usuario autenticado intenta acceder a /login, será redirigido al dashboard (o a la ruta configurada en RouteServiceProvider).

Personalizar la redirección

Por defecto, cuando un usuario no autenticado intenta acceder a una ruta protegida, Laravel lo redirige a /login. Puedes cambiar esto en bootstrap/app.php:

php
<?php

// bootstrap/app.php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->redirectGuestsTo('/iniciar-sesion');
        $middleware->redirectUsersTo('/panel');
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

Con esta configuración:

  • redirectGuestsTo: A dónde enviar usuarios no autenticados que intentan acceder a rutas protegidas
  • redirectUsersTo: A dónde enviar usuarios autenticados que intentan acceder a rutas de invitado (como login)

Redirección inteligente con intended

Cuando un usuario intenta acceder a una página protegida, Laravel guarda la URL a la que quería ir. Después de hacer login, puedes redirigirlo a esa URL con intended():

php
<?php

declare(strict_types=1);

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (Auth::attempt($credentials)) {
            $request->session()->regenerate();

            // Redirige a la URL original o al dashboard
            return redirect()->intended(route('dashboard'));
        }

        return back()->withErrors([
            'email' => 'Las credenciales no coinciden.',
        ]);
    }
}

Por ejemplo: el usuario intenta ir a /settings, es redirigido a /login, hace login, y automáticamente es llevado a /settings (no al dashboard).

Verificar autenticación en vistas

En tus plantillas Blade, puedes usar directivas para mostrar contenido según el estado de autenticación:

blade
<nav>
    @auth
        {{-- Usuario autenticado --}}
        <span>Hola, {{ auth()->user()->name }}</span>
        <a href="{{ route('dashboard') }}">Panel</a>
        <form method="POST" action="{{ route('logout') }}">
            @csrf
            <button type="submit">Cerrar sesión</button>
        </form>
    @else
        {{-- Usuario invitado --}}
        <a href="{{ route('login') }}">Iniciar sesión</a>
        <a href="{{ route('register') }}">Registrarse</a>
    @endauth
</nav>

También existe @guest para el caso contrario:

blade
@guest
    <p>Bienvenido. Por favor, inicia sesión para continuar.</p>
@endguest

Acceder al usuario autenticado

En tus controladores y vistas, puedes acceder al usuario autenticado de varias formas:

php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

// En un controlador

// Opción 1: Facade Auth
$user = Auth::user();
$userId = Auth::id();

// Opción 2: Helper auth()
$user = auth()->user();
$userId = auth()->id();

// Opción 3: Desde el Request (recomendado en controladores)
public function show(Request $request)
{
    $user = $request->user();
    return view('profile', compact('user'));
}

// Verificar si hay usuario autenticado
if (Auth::check()) {
    // Usuario autenticado
}

// O con el helper
if (auth()->check()) {
    // Usuario autenticado
}

Resumen

  • El middleware auth protege rutas para usuarios autenticados
  • Aplícalo con ->middleware('auth') en rutas individuales
  • Usa Route::middleware('auth')->group() para agrupar varias rutas
  • El middleware verified requiere email verificado
  • El middleware guest es para usuarios no autenticados
  • Con rutas resource, usa ->only() y ->except() para controlar qué rutas proteger
  • Usa redirect()->intended() para volver a la URL original tras login
  • Las directivas @auth y @guest controlan contenido en vistas

Ejercicios

Ejercicio 1: Crear un área privada

Crea tres rutas protegidas: /account, /account/orders y /account/settings. Todas deben requerir autenticación y usar el prefijo "account" con nombres de ruta apropiados.

Ver solución
<?php

// routes/web.php

use Illuminate\Support\Facades\Route;

Route::middleware('auth')
    ->prefix('account')
    ->name('account.')
    ->group(function () {
        Route::get('/', function () {
            return view('account.index');
        })->name('index');

        Route::get('/orders', function () {
            return view('account.orders');
        })->name('orders');

        Route::get('/settings', function () {
            return view('account.settings');
        })->name('settings');
    });

// Resultado:
// GET /account -> account.index
// GET /account/orders -> account.orders
// GET /account/settings -> account.settings

Ejercicio 2: Rutas resource con middleware parcial

Configura las rutas para un PostController donde index y show sean públicos, pero create, store, edit, update y destroy requieran autenticación. Usa rutas resource.

Ver solución
<?php

// routes/web.php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

// Rutas públicas: listar y ver posts
Route::resource('posts', PostController::class)
    ->only(['index', 'show']);

// Rutas protegidas: crear, editar, eliminar posts
Route::resource('posts', PostController::class)
    ->except(['index', 'show'])
    ->middleware('auth');
<?php

// app/Http/Controllers/PostController.php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PostController extends Controller
{
    public function index(): View
    {
        $posts = Post::latest()->paginate(10);
        return view('posts.index', compact('posts'));
    }

    public function show(Post $post): View
    {
        return view('posts.show', compact('post'));
    }

    public function create(): View
    {
        return view('posts.create');
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'title' => ['required', 'max:255'],
            'content' => ['required'],
        ]);

        $request->user()->posts()->create($validated);

        return redirect()->route('posts.index');
    }

    public function edit(Post $post): View
    {
        return view('posts.edit', compact('post'));
    }

    public function update(Request $request, Post $post): RedirectResponse
    {
        $validated = $request->validate([
            'title' => ['required', 'max:255'],
            'content' => ['required'],
        ]);

        $post->update($validated);

        return redirect()->route('posts.show', $post);
    }

    public function destroy(Post $post): RedirectResponse
    {
        $post->delete();

        return redirect()->route('posts.index');
    }
}

Ejercicio 3: Navegación dinámica

Crea un componente de navegación Blade que muestre enlaces diferentes según si el usuario está autenticado o no. Para usuarios autenticados: Dashboard, Perfil y botón Cerrar sesión. Para invitados: Iniciar sesión y Registrarse.

Ver solución
{{-- resources/views/components/navigation.blade.php --}}

<nav class="main-nav">
    <a href="{{ url('/') }}">Inicio</a>

    @auth
        <a href="{{ route('dashboard') }}">Dashboard</a>
        <a href="{{ route('profile.edit') }}">
            {{ auth()->user()->name }}
        </a>

        <form method="POST" action="{{ route('logout') }}" class="inline">
            @csrf
            <button type="submit">Cerrar sesión</button>
        </form>
    @else
        <a href="{{ route('login') }}">Iniciar sesión</a>
        <a href="{{ route('register') }}">Registrarse</a>
    @endauth
</nav>
{{-- Usar en el layout principal --}}

<!DOCTYPE html>
<html>
<head>
    <title>Mi App</title>
</head>
<body>
    <x-navigation />

    <main>
        {{ $slot }}
    </main>
</body>
</html>

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales, soporte personalizado y certificado.

Descubrir cursos premium