Middlewares e autenticação no Laravel

1. Fundamentos dos Middlewares no Laravel

Middlewares são camadas intermediárias que filtram requisições HTTP antes que elas atinjam o controller. No ecossistema PHP, o Laravel implementa esse padrão de forma elegante, permitindo inspecionar, modificar ou bloquear requisições conforme regras de negócio.

Para criar um middleware personalizado, utilize o Artisan:

php artisan make:middleware CheckUserRole

O arquivo gerado em app/Http/Middleware/CheckUserRole.php contém o método handle():

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class CheckUserRole
{
    public function handle(Request $request, Closure $next, string $role): mixed
    {
        if (!$request->user() || !$request->user()->hasRole($role)) {
            abort(403, 'Acesso não autorizado.');
        }

        return $next($request);
    }
}

O registro pode ser feito de três formas:

  • Globais: no array $middleware do Kernel, aplicam-se a todas as rotas.
  • Grupos: no array $middlewareGroups (ex: web, api).
  • Nomeados: no array $routeMiddleware, permitindo uso direto nas rotas.
// app/Http/Kernel.php
protected $routeMiddleware = [
    'role' => \App\Http\Middleware\CheckUserRole::class,
];

2. Mecanismos de Funcionamento dos Middlewares

O ciclo de vida de uma requisição no Laravel segue uma pilha de middlewares. O código antes de return $next($request) executa antes do controller; o código após, depois.

public function handle(Request $request, Closure $next): mixed
{
    // Antes do controller
    Log::info('Requisição recebida: ' . $request->path());

    $response = $next($request);

    // Depois do controller
    Log::info('Resposta enviada: ' . $response->getStatusCode());

    return $response;
}

Middlewares podem receber parâmetros adicionais, como vimos com $role. Outro exemplo prático é limitar acesso por horário:

// Rota
Route::get('/admin', function () {
    return 'Painel administrativo';
})->middleware('check.hour:08:00,18:00');

3. Middlewares Nativos Essenciais

O Laravel fornece middlewares prontos que resolvem problemas comuns de autenticação e segurança:

// Proteger rotas autenticadas
Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware('auth');

// Rotas apenas para visitantes não autenticados
Route::get('/login', function () {
    return view('auth.login');
})->middleware('guest');

// Limitar requisições (ex: 10 por minuto)
Route::post('/api/upload', function () {
    // ...
})->middleware('throttle:10,1');

// Exigir e-mail verificado
Route::get('/settings', function () {
    return view('settings');
})->middleware(['auth', 'verified']);

// Confirmar senha antes de ação sensível
Route::post('/delete-account', function () {
    // ...
})->middleware('password.confirm');

4. Autenticação com o Scaffold do Laravel

Para agilizar o desenvolvimento, o Laravel oferece pacotes de scaffolding. O Laravel Breeze é a opção mais leve e moderna:

composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install && npm run dev

Isso gera controllers como LoginController, RegisterController e views Blade completas. A estrutura gerada segue o padrão:

// app/Http/Controllers/Auth/AuthenticatedSessionController.php
public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();
    $request->session()->regenerate();
    return redirect()->intended(RouteServiceProvider::HOME);
}

Para personalizar o fluxo, basta modificar os controllers ou publicar as views:

php artisan vendor:publish --tag=laravel-breeze-views

5. Guards e Providers de Autenticação

O Laravel suporta múltiplos sistemas de autenticação simultaneamente. A configuração fica em config/auth.php:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'sanctum',
        'provider' => 'users',
    ],
    'admin' => [
        'driver' => 'session',
        'provider' => 'admins',
    ],
],

Os providers definem como os usuários são recuperados:

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
    'admins' => [
        'driver' => 'database',
        'table' => 'admins',
    ],
],

A diferença entre autenticação stateful (sessão) e stateless (tokens) é crucial:

  • Stateful: mantém estado via sessão PHP, ideal para aplicações web tradicionais.
  • Stateless: cada requisição carrega um token, comum em APIs REST.

6. Proteção de Rotas com Middleware de Autenticação

Aplicar middlewares em rotas é simples e flexível:

// Em uma rota específica
Route::get('/profile', [ProfileController::class, 'show'])->middleware('auth');

// Em grupos de rotas
Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::resource('/posts', PostController::class);
});

// Com Gate para autorização granular
Route::put('/posts/{post}', function (Post $post) {
    $this->authorize('update', $post);
    // ...
})->middleware('auth');

Para redirecionamento personalizado, modifique o método redirectTo() no App\Http\Middleware\Authenticate.php:

protected function redirectTo(Request $request): ?string
{
    if (!$request->expectsJson()) {
        return route('login.custom');
    }
}

7. Autenticação via API com Sanctum

O Laravel Sanctum é a solução oficial para autenticação de APIs e SPAs. Instalação:

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

Para emitir tokens:

// UserController.php
public function login(Request $request)
{
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
    ]);

    if (!Auth::attempt($request->only('email', 'password'))) {
        return response()->json(['message' => 'Credenciais inválidas'], 401);
    }

    $user = Auth::user();
    $token = $user->createToken('api-token')->plainTextToken;

    return response()->json(['token' => $token]);
}

Proteção de rotas:

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

    Route::post('/posts', [PostController::class, 'store']);
});

Para SPAs, o Sanctum utiliza cookies de sessão, dispensando tokens explícitos:

// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:3000')),

8. Boas Práticas e Depuração

Organize middlewares em módulos para aplicações grandes:

// app/Http/Middleware/Admin/VerifyAdmin.php
namespace App\Http\Middleware\Admin;

class VerifyAdmin
{
    public function handle($request, Closure $next)
    {
        if (!$request->user()->isAdmin()) {
            abort(403);
        }
        return $next($request);
    }
}

Testes com PHPUnit garantem que middlewares funcionem corretamente:

public function test_middleware_blocks_unauthorized_access()
{
    $response = $this->get('/admin/dashboard');
    $response->assertRedirect('/login');

    $user = User::factory()->create();
    $response = $this->actingAs($user)->get('/admin/dashboard');
    $response->assertStatus(403);
}

Para depuração, utilize logs estratégicos:

Log::channel('auth')->info('Falha de autenticação', [
    'ip' => $request->ip(),
    'email' => $request->email,
    'time' => now(),
]);

Referências