verified_user 10. Middleware de Autenticação

Middleware é o guardião da sua API. É um código que executa antes de cada endpoint protegido, verificando se o usuário está autenticado e autorizado. Sem middleware, você teria que copiar a mesma lógica de validação JWT em todos os arquivos, criando código duplicado e pontos de falha.

Um middleware profissional faz quatro coisas: extrai o token JWT do header Authorization, valida sua assinatura e expiração, decodifica o payload para obter o ID do usuário e retorna 401 Unauthorized se qualquer etapa falhar. Ele também deve implementar rate limiting, logging de acessos e blacklist de tokens revogados.

Nesta seção final, você aprenderá a construir um sistema completo de middleware com autorização baseada em roles (RBAC), cache de permissões, rate limiting por IP e integração transparente com todos os endpoints CRUD criados nas seções anteriores.

security
🔐 Princípio de Segurança: "Deny by default, allow explicitly". Todas as rotas devem ser protegidas por padrão, exceto endpoints públicos como login e registro que você explicitamente marca como públicos.

route Arquitetura de Middleware

┌─────────────────────────────────────────────────────────────────┐
│                    REQUISIÇÃO DO FRONTEND                        │
│  POST /api/posts/create                                         │
│  Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...  │
└────────────────────────┬────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│              MIDDLEWARE 1: CORS Headers                          │
│  ✅ Verifica origem permitida                                   │
│  ✅ Adiciona headers Access-Control-*                           │
└────────────────────────┬────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│              MIDDLEWARE 2: Rate Limiting                         │
│  ✅ Verifica tentativas por IP (max 100/min)                    │
│  ❌ Se exceder → 429 Too Many Requests                          │
└────────────────────────┬────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│              MIDDLEWARE 3: JWT Verification                      │
│  1. Extrai token do header Authorization                        │
│  2. Valida assinatura HMAC                                      │
│  3. Verifica expiração (exp)                                    │
│  4. Decodifica payload → user_id                                │
│  ❌ Se inválido → 401 Unauthorized                              │
└────────────────────────┬────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│              MIDDLEWARE 4: Authorization (RBAC)                  │
│  ✅ Verifica role do usuário (user/admin/moderator)             │
│  ✅ Checa permissões específicas (can_create_posts)             │
│  ❌ Se sem permissão → 403 Forbidden                            │
└────────────────────────┬────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│                  ENDPOINT (posts/create.php)                     │
│  🎯 Lógica de negócio executa aqui                              │
│  📊 user_id está disponível via $GLOBALS['user_id']             │
└─────────────────────────────────────────────────────────────────┘

code Implementando Middleware JWT

 false,
            'message' => 'Token não fornecido',
            'code' => 'NO_TOKEN'
        ]);
        exit;
    }
    
    // 2. VALIDAR FORMATO "Bearer TOKEN"
    if (!preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
        http_response_code(401);
        echo json_encode([
            'success' => false,
            'message' => 'Formato de token inválido (use: Bearer TOKEN)',
            'code' => 'INVALID_FORMAT'
        ]);
        exit;
    }
    
    $token = $matches[1];
    
    // 3. VALIDAR JWT
    $decoded = validateJWT($token);
    
    if (!$decoded) {
        http_response_code(401);
        echo json_encode([
            'success' => false,
            'message' => 'Token inválido ou expirado',
            'code' => 'INVALID_TOKEN'
        ]);
        exit;
    }
    
    // 4. EXTRAIR USER_ID
    $userId = $decoded->sub ?? null;
    
    if (!$userId) {
        http_response_code(401);
        echo json_encode([
            'success' => false,
            'message' => 'Token não contém user_id',
            'code' => 'MALFORMED_TOKEN'
        ]);
        exit;
    }
    
    // 5. VERIFICAR SE USUÁRIO AINDA EXISTE E ESTÁ ATIVO
    global $pdo;
    $stmt = $pdo->prepare('SELECT id, active FROM users WHERE id = ?');
    $stmt->execute([$userId]);
    $user = $stmt->fetch();
    
    if (!$user || !$user['active']) {
        http_response_code(401);
        echo json_encode([
            'success' => false,
            'message' => 'Usuário inativo ou não encontrado',
            'code' => 'USER_INACTIVE'
        ]);
        exit;
    }
    
    // 6. (OPCIONAL) VERIFICAR BLACKLIST DE TOKENS REVOGADOS
    $stmt = $pdo->prepare('
        SELECT id FROM token_blacklist 
        WHERE token = ? AND expires_at > NOW()
    ');
    $stmt->execute([hash('sha256', $token)]);
    
    if ($stmt->fetch()) {
        http_response_code(401);
        echo json_encode([
            'success' => false,
            'message' => 'Token foi revogado (logout forçado)',
            'code' => 'TOKEN_REVOKED'
        ]);
        exit;
    }
    
    // 7. LOG DE ACESSO (opcional, para auditoria)
    try {
        $pdo->prepare('
            INSERT INTO access_logs (user_id, endpoint, method, ip, user_agent, created_at) 
            VALUES (?, ?, ?, ?, ?, NOW())
        ')->execute([
            $userId,
            $_SERVER['REQUEST_URI'] ?? '',
            $_SERVER['REQUEST_METHOD'] ?? '',
            $_SERVER['REMOTE_ADDR'] ?? '',
            $_SERVER['HTTP_USER_AGENT'] ?? ''
        ]);
    } catch (Exception $e) {
        // Log, mas não bloquear requisição
        error_log("Access log error: " . $e->getMessage());
    }
    
    // ✅ AUTENTICAÇÃO BEM-SUCEDIDA
    return $userId;
}

/**
 * Middleware de autorização baseada em role
 * 
 * @param array $allowedRoles - Roles permitidas ['admin', 'moderator']
 * @return void - Encerra com 403 se não autorizado
 */
function requireRole($allowedRoles = []) {
    $userId = verifyAuth(); // Garante autenticação primeiro
    
    global $pdo;
    $stmt = $pdo->prepare('SELECT role FROM users WHERE id = ?');
    $stmt->execute([$userId]);
    $userRole = $stmt->fetchColumn();
    
    if (!in_array($userRole, $allowedRoles)) {
        http_response_code(403);
        echo json_encode([
            'success' => false,
            'message' => 'Sem permissão para acessar este recurso',
            'code' => 'FORBIDDEN',
            'required_roles' => $allowedRoles,
            'your_role' => $userRole
        ]);
        exit;
    }
}
?>
check_circle
✅ Recursos Implementados:
  • Extração e validação de token JWT
  • Verificação de usuário ativo no banco
  • Blacklist de tokens revogados
  • Log de acessos para auditoria
  • Autorização baseada em roles (RBAC)

integration_instructions Usando o Middleware nos Endpoints

lock_open Endpoint Público

query('
    SELECT * FROM posts 
    WHERE status = "published"
    LIMIT 10
');
echo json_encode($stmt->fetchAll());
?>

lock Endpoint Protegido

 'Post criado',
    'user_id' => $userId
]);
?>

admin_panel_settings Endpoint Admin-Only

query('SELECT * FROM users');
echo json_encode($stmt->fetchAll());
?>

speed Rate Limiting - Prevenir Abuso

= 100) {
            http_response_code(429); // Too Many Requests
            echo json_encode([
                'success' => false,
                'message' => 'Muitas requisições. Tente novamente em 1 minuto.',
                'code' => 'RATE_LIMIT_EXCEEDED',
                'retry_after' => 60
            ]);
            header('Retry-After: 60');
            exit;
        }
        
        apcu_inc($key, 1, $success, 60); // Incrementa e expira em 60s
    }
}

// USO: Chamar no início de cada endpoint
checkRateLimit();
?>
warning
⚠️ Alternativas Robustas: Para produção, use Redis com algoritmo Token Bucket ou Cloudflare Rate Limiting no nível de CDN para proteger contra DDoS.

logout Revogação de Tokens (Logout)

exp);
        
        $pdo->prepare('
            INSERT INTO token_blacklist (token, user_id, expires_at, created_at) 
            VALUES (?, ?, ?, NOW())
        ')->execute([$tokenHash, $userId, $expiresAt]);
        
        echo json_encode([
            'success' => true,
            'message' => 'Logout realizado com sucesso'
        ]);
    }
    
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode(['success' => false, 'message' => 'Erro ao fazer logout']);
}
?>
-- Tabela de blacklist de tokens
CREATE TABLE token_blacklist (
    id INT AUTO_INCREMENT PRIMARY KEY,
    token CHAR(64) NOT NULL,  -- SHA256 hash
    user_id INT NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_token (token),
    INDEX idx_expires (expires_at),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- Job de limpeza (executar diariamente)
DELETE FROM token_blacklist WHERE expires_at < NOW();

shield Permissões Granulares (RBAC Avançado)

prepare('SELECT role FROM users WHERE id = ?');
    $stmt->execute([$userId]);
    $role = $stmt->fetchColumn();
    
    // Definir permissões por role (pode vir do banco)
    $rolePermissions = [
        'admin' => ['*'], // Todas permissões
        'moderator' => [
            'posts.create', 'posts.update', 'posts.delete',
            'comments.delete', 'users.view'
        ],
        'user' => [
            'posts.create', 'posts.update-own', 'posts.delete-own',
            'comments.create'
        ]
    ];
    
    $userPermissions = $rolePermissions[$role] ?? [];
    
    // Verificar se tem permissão (ou wildcard *)
    return in_array('*', $userPermissions) || in_array($permission, $userPermissions);
}

/**
 * Middleware que requer permissão específica
 */
function requirePermission($permission) {
    if (!hasPermission($permission)) {
        http_response_code(403);
        echo json_encode([
            'success' => false,
            'message' => "Sem permissão: $permission",
            'code' => 'PERMISSION_DENIED'
        ]);
        exit;
    }
}

// USO:
// api/posts/create.php
requirePermission('posts.create');
?>

Admin

Permissões:
• Todas (*)
• Gerenciar usuários
• Configurações sistema

Moderator

Permissões:
• Editar/deletar posts
• Moderar comentários
• Ver usuários

User

Permissões:
• Criar posts
• Editar próprios posts
• Comentar

summarize Checklist Final de Segurança

✅ Backend (PHP)

  • ☑️ PDO com prepared statements
  • ☑️ Validação de todos inputs
  • ☑️ Sanitização de dados
  • ☑️ JWT com secret forte (256+ bits)
  • ☑️ CORS configurado corretamente
  • ☑️ HTTPS obrigatório em produção
  • ☑️ Rate limiting implementado
  • ☑️ Logs de auditoria
  • ☑️ Backups automáticos

✅ Frontend (React)

  • ☑️ Token em localStorage (ou httpOnly cookie)
  • ☑️ Axios interceptors para token
  • ☑️ Refresh automático em 401
  • ☑️ Validação client-side (UX)
  • ☑️ Sanitização XSS (DOMPurify)
  • ☑️ Variáveis .env (não commitar)
  • ☑️ CSP headers configurados
  • ☑️ Confirmação em deletes
  • ☑️ Loading states adequados
celebration
🎉 Parabéns! Você Dominou PHP + React

Você agora possui conhecimento profissional para construir aplicações full-stack seguras e escaláveis. Este artigo cobriu todos os fundamentos essenciais:

✅ Arquitetura Frontend + Backend
✅ Conexão Segura com PDO
✅ Autenticação JWT
✅ Hash de Senhas bcrypt
✅ CRUD Completo (Create/Read/Update/Delete)
✅ Middleware de Proteção

🚀 Próximos Passos: Deploy em produção, CI/CD, testes automatizados, Docker, Kubernetes...