Referência v3
Voltar à plataforma

Middlewares

Todos os middlewares disponíveis no projeto — o que cada um faz, quando usar e como implementar nas suas rotas.

O que é um middleware?

Um middleware é uma camada que intercepta a requisição HTTP antes de chegar ao Controller. Você pode encadear vários middlewares em uma rota — eles executam em ordem, e cada um decide se passa a requisição adiante ou retorna uma resposta imediatamente.

Como usar middlewares nas rotas

Forma recomendada — fachada Auth

Use Src\Kernel\Auth para proteger rotas com uma linha. Sem imports longos, sem arrays complexos.

php
<?php
use Src\Kernel\Auth;

// Rota pública — sem proteção
$router->get('/api/produtos', [Controller::class, 'listar']);

// Rota privada — qualquer usuário logado
$router->get('/api/perfil', [Controller::class, 'perfil'], Auth::user());

// Rota admin — só admin_system
$router->post('/api/admin/produtos', [Controller::class, 'criar'], Auth::admin());

// Rota com rate limit (10 req/min) + auth
$router->post('/api/produtos', [Controller::class, 'criar'], Auth::user(limit: 10));

// Rota admin com rate limit + proteção de banco
$router->delete('/api/admin/produtos/{id}', [Controller::class, 'deletar'], Auth::admin(limit: 5, db: true));

// Rota de API (token machine-to-machine)
$router->post('/api/webhook', [Controller::class, 'receber'], Auth::api());

// Rota pública com conteúdo diferente para logados
$router->get('/api/feed', [Controller::class, 'feed'], Auth::optional());

// Só rate limit — sem autenticação
$router->post('/api/contato', [Controller::class, 'enviar'], Auth::limit(5));

Acessando o usuário no Controller

php
<?php
use Src\Kernel\Auth;

class ProdutoController
{
    public function criar(Request $request): Response
    {
        // Obtém o usuário logado (ou null se não autenticado)
        $user = Auth::current($request);

        // Obtém só o ID
        $userId = Auth::id($request);

        // Obtém o nível de acesso
        $role = Auth::role($request);

        // Lança 401 se não autenticado, retorna o usuário se sim
        $user = Auth::check($request);

        // Lança 403 se não for admin_system
        $user = Auth::checkAdmin($request);

        // Lança 403 se não tiver um dos papéis informados
        $user = Auth::checkRole($request, 'admin', 'moderador');

        return Response::json(['criado_por' => $userId]);
    }
}

Forma manual (avançado)

Se precisar de controle total, ainda é possível usar os middlewares diretamente:

php
<?php
use Src\Kernel\Middlewares\AuthHybridMiddleware;
use Src\Kernel\Middlewares\AdminOnlyMiddleware;
use Src\Kernel\Middlewares\RateLimitMiddleware;

$rateLimit = [RateLimitMiddleware::class, ['limit' => 5, 'window' => 60, 'key' => 'recurso.criar']];
$router->post('/api/admin/recurso', [Controller::class, 'criar'], [
    $rateLimit,
    AuthHybridMiddleware::class,
    AdminOnlyMiddleware::class,
]);

Visão geral de todos os middlewares

MiddlewareFinalidadeUso típico
AuthHybridMiddlewareValida JWT e injeta usuário autenticadoQualquer rota que exige login
AdminOnlyMiddlewareExige nivel_acesso = admin_systemRotas de gerenciamento admin
ApiTokenMiddlewareValida token JWT de API (JWT_API_SECRET)Integrações machine-to-machine
AuthCookieMiddlewareValida JWT via cookie auth_tokenRotas de página com cookie de sessão
AuthPageMiddlewareAuth para páginas HTML — redireciona em vez de 401Dashboard e páginas protegidas
OptionalAuthHybridMiddlewareTenta autenticar mas não bloqueia se não houver tokenRotas com conteúdo diferente para logados
RateLimitMiddlewareLimita requisições por IP e usuárioRegistro, login, qualquer rota pública
CircuitBreakerMiddlewareProtege o banco de falhas em cascataRotas que dependem de banco ou serviços externos
BotBlockerMiddlewareBloqueia scanners e ferramentas maliciosasGlobal — aplicado automaticamente pelo Kernel
HttpsEnforcerMiddlewareBloqueia HTTP quando HTTPS é obrigatórioGlobal — aplicado automaticamente em produção
RouteProtectionMiddlewareValida JWT com restrição por papel (role)Rotas com múltiplos níveis de acesso
SecurityHeadersMiddlewareGarante headers de segurança em toda respostaGlobal — aplicado automaticamente pelo Kernel

AuthHybridMiddleware

O middleware de autenticação principal. Valida o JWT enviado no header Authorization: Bearer TOKEN ou no cookie auth_token, verifica se o token não foi revogado (blacklist) e injeta o usuário autenticado na requisição.

O que ele injeta na requisição

Após passar por este middleware, o Controller pode acessar:
$request->attribute('auth_user') — objeto do usuário autenticado
$request->attribute('auth_payload') — payload decodificado do JWT
$request->attribute('token_signed_with_api_secret')true se assinado com JWT_API_SECRET

php
use Src\Kernel\Middlewares\AuthHybridMiddleware;

// Rota que exige qualquer usuário logado
$router->get('/api/perfil', [MeuController::class, 'perfil'], [AuthHybridMiddleware::class]);

// No Controller — acessa o usuário autenticado
public function perfil(Request $request): Response
{
    $usuario = $request->attribute('auth_user'); // objeto Usuario
    $payload = $request->attribute('auth_payload'); // stdClass com sub, nivel_acesso, etc.

    return Response::json(['nome' => $usuario->getNomeCompleto()]);
}

// Respostas possíveis:
// 401 — token ausente, expirado ou revogado
// 200 — usuário autenticado, requisição passa para o Controller

AdminOnlyMiddleware

Deve ser usado sempre após o AuthHybridMiddleware. Verifica se o usuário autenticado tem nivel_acesso = admin_system e se o token foi assinado com JWT_API_SECRET. Tokens de usuários comuns são rejeitados com 403.

php
use Src\Kernel\Middlewares\AuthHybridMiddleware;
use Src\Kernel\Middlewares\AdminOnlyMiddleware;

// Sempre use AuthHybridMiddleware ANTES do AdminOnlyMiddleware
$router->get('/api/admin/usuarios', [Controller::class, 'listar'], [
    AuthHybridMiddleware::class,  // 1º: valida o JWT
    AdminOnlyMiddleware::class,   // 2º: verifica se é admin_system
]);

// Respostas possíveis:
// 401 — token ausente ou inválido (AuthHybridMiddleware)
// 403 — usuário autenticado mas não é admin_system
// 200 — admin autenticado, requisição passa

RateLimitMiddleware

Limita o número de requisições por IP e por usuário autenticado em uma janela de tempo. Usa Redis quando disponível (distribuído entre múltiplos servidores) ou arquivo local como fallback. Adiciona headers X-RateLimit-* em todas as respostas.

OpçãoTipoPadrãoDescrição
limitint60Máximo de requisições por janela
windowint60Janela de tempo em segundos
keystringURI da rotaPrefixo único para o contador (evita colisão entre rotas)
user_limitintigual a limitLimite específico por usuário autenticado
php
use Src\Kernel\Middlewares\RateLimitMiddleware;

// 5 requisições por minuto por IP — ideal para registro
$registroLimit = [RateLimitMiddleware::class, [
    'limit'  => 5,
    'window' => 60,
    'key'    => 'meu.registro',
]];
$router->post('/api/registrar', [Controller::class, 'criar'], [$registroLimit]);

// 10 req/min por IP, 3 por usuário autenticado
$loginLimit = [RateLimitMiddleware::class, [
    'limit'      => 10,
    'window'     => 60,
    'key'        => 'meu.login',
    'user_limit' => 3,
]];
$router->post('/api/login', [Controller::class, 'login'], [$loginLimit]);

// Respostas possíveis:
// 429 — limite atingido, com header Retry-After indicando quando tentar novamente
// Headers adicionados em toda resposta:
//   X-RateLimit-Limit: 10
//   X-RateLimit-Remaining: 7
//   X-RateLimit-Reset: 1712345678 (timestamp Unix)

CircuitBreakerMiddleware

Implementa o padrão Circuit Breaker para proteger o banco de dados e serviços externos de falhas em cascata. Tem três estados: CLOSED (normal), OPEN (bloqueado após muitas falhas) e HALF (testando recuperação após o cooldown).

OpçãoTipoPadrãoDescrição
servicestring'default'Nome do serviço monitorado (ex: 'database', 'email')
thresholdint5Número de falhas consecutivas para abrir o circuito
cooldownint30Segundos aguardando antes de tentar novamente (estado HALF)
php
use Src\Kernel\Middlewares\CircuitBreakerMiddleware;

// Protege rotas que dependem do banco — abre após 5 falhas, cooldown de 20s
$dbCircuit = [CircuitBreakerMiddleware::class, [
    'service'   => 'database',
    'threshold' => 5,
    'cooldown'  => 20,
]];
$router->post('/api/registrar', [Controller::class, 'criar'], [$dbCircuit]);

// Protege chamadas a serviço externo (ex: API de pagamento)
$pagCircuit = [CircuitBreakerMiddleware::class, [
    'service'   => 'pagamento',
    'threshold' => 3,
    'cooldown'  => 60,
]];
$router->post('/api/pagamento', [Controller::class, 'pagar'], [$pagCircuit]);

// Respostas possíveis:
// 503 — circuito aberto, com header Retry-After
// Passa normalmente quando CLOSED ou HALF

ApiTokenMiddleware

Valida tokens JWT gerados com JWT_API_SECRET (tokens de API, não de usuário). Usado para integrações machine-to-machine onde não há um usuário humano fazendo login. Aceita o token via header Authorization: Bearer TOKEN ou X-API-KEY: TOKEN.

php
use Src\Kernel\Middlewares\ApiTokenMiddleware;

// Rota acessível apenas por tokens de API (integrações, webhooks, etc.)
$router->post('/api/webhook/pagamento', [Controller::class, 'receber'], [ApiTokenMiddleware::class]);

// Respostas possíveis:
// 401 — token ausente ou inválido
// 403 — token válido mas não é de API (é de usuário)
// 500 — JWT_API_SECRET não configurado no .env

Como gerar o token JWT_API_SECRET

Rotas protegidas por AdminOnlyMiddleware ou ApiTokenMiddleware exigem um token assinado com JWT_API_SECRET. Há três formas de gerar esse token:

Opção 1 — Via CLI (recomendado)

O jeito mais simples. O sistema gera e exibe o token pronto para usar:

bash
# Abre o menu interativo e escolha a opção 11
php vupi setup

# Ou direto, sem menu:
php vupi setup --auto --api-token=generate

O token gerado tem validade de 1 hora e payload:

json
{
  "sub": "api_user_id",
  "exp": 1712345678,
  "api_access": true,
  "tipo": "api"
}

Opção 2 — Via login do admin_system

Para rotas com AdminOnlyMiddleware, faça login com o usuário admin_system. O token retornado já é assinado com JWT_API_SECRET e funciona nessas rotas:

bash
# curl
curl -X POST http://localhost:3005/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","senha":"admin123"}'

# HTTPie
http POST http://localhost:3005/api/auth/login \
  [email protected] senha=admin123
json
// Resposta — copie o "access_token":
{
  "status": "success",
  "access_token": "eyJ...",   // ← use este nas rotas admin
  "token_type": "Bearer",
  "expires_in": 900
}

// Use assim nas requisições:
// Authorization: Bearer eyJ...
Qual token usar em cada caso?

Rotas com AdminOnlyMiddleware (ex: GET /api/usuarios) → use o token do login do admin_system via /api/auth/login.
Rotas com ApiTokenMiddleware (ex: webhooks, integrações) → use o token gerado via php vupi setup opção 11.
Rotas com AuthHybridMiddleware (ex: GET /api/perfil) → use o token de qualquer usuário logado via /api/login.

Opção 3 — Gerar manualmente em PHP

Útil para scripts de integração ou testes automatizados:

php
<?php
require __DIR__ . '/vendor/autoload.php';

use Firebase\JWT\JWT;

$secret = $_ENV['JWT_API_SECRET'] ?? 'seu_jwt_api_secret_aqui';

// Token de API (para ApiTokenMiddleware)
$tokenApi = JWT::encode([
    'sub'        => 'minha_integracao',
    'exp'        => time() + 3600,  // expira em 1 hora
    'api_access' => true,
    'tipo'       => 'api',
], $secret, 'HS256');

echo "Token de API:\n" . $tokenApi . "\n";
Nunca exponha o JWT_API_SECRET

O JWT_API_SECRET fica no .env e nunca deve ser commitado no repositório. Tokens gerados com ele têm acesso total às rotas admin — trate-os como senhas.

OptionalAuthHybridMiddleware

Tenta autenticar o usuário, mas não bloqueia se o token estiver ausente ou inválido. Útil para rotas que retornam conteúdo diferente para usuários logados e não logados — como um feed público que mostra mais detalhes para quem está autenticado.

php
use Src\Kernel\Middlewares\OptionalAuthHybridMiddleware;

// Rota pública, mas com conteúdo extra para usuários logados
$router->get('/api/feed', [Controller::class, 'feed'], [OptionalAuthHybridMiddleware::class]);

// No Controller — verifica se o usuário está autenticado
public function feed(Request $request): Response
{
    $usuario = $request->attribute('auth_user'); // null se não autenticado

    if ($usuario !== null) {
        // Retorna feed personalizado para o usuário logado
        return Response::json(['feed' => $this->feedPersonalizado($usuario)]);
    }

    // Retorna feed público genérico
    return Response::json(['feed' => $this->feedPublico()]);
}

Variante do AuthHybridMiddleware que lê o token exclusivamente do cookie auth_token (em vez do header Authorization). Usado internamente pelo sistema para rotas de página que dependem de cookie de sessão.

php
use Src\Kernel\Middlewares\AuthCookieMiddleware;

// Rota que autentica via cookie (ex: endpoint chamado por página HTML)
$router->get('/api/minha-conta', [Controller::class, 'conta'], [AuthCookieMiddleware::class]);

// O token deve estar no cookie 'auth_token' — definido pelo módulo Auth no login
// Respostas possíveis:
// 401 — cookie ausente, token expirado ou revogado

AuthPageMiddleware

Middleware de autenticação para rotas de página HTML (não API). Quando a identidade está ausente ou inválida, redireciona para /ide/login. A restrição administrativa é responsabilidade do AdminOnlyMiddleware, aplicado separadamente nas páginas de administração.

php
use Src\Kernel\Middlewares\AuthPageMiddleware;

// Rota de página HTML protegida — redireciona para / se não autenticado
$router->get('/meu-painel', [PainelController::class, 'index'], [AuthPageMiddleware::class]);

// Comportamento:
// Não autenticado → redirect 302 para /
// Autenticado mas não admin_system → redirect 302 para /
// Autenticado como admin_system → passa para o Controller

RouteProtectionMiddleware

Valida assinatura e claims do JWT e permite restringir acesso por papel (role). Tokens de usuário também são consultados na blacklist quando o repositório de revogação está disponível. Token de API puro não aceita restrição por papel; use escopos e o middleware adequado à integração.

php
use Src\Kernel\Middlewares\RouteProtectionMiddleware;

// Sem restrição de papel — qualquer token válido passa
$router->get('/api/recurso', [Controller::class, 'listar'], [RouteProtectionMiddleware::class]);

// Restrito a um papel específico
$request = $request->withAttribute('roles', ['admin', 'moderador']);
$router->put('/api/recurso/{id}', [Controller::class, 'atualizar'], [RouteProtectionMiddleware::class]);

// Respostas possíveis:
// 401 — token ausente ou inválido
// 403 — papel do usuário não está na lista de roles permitidos

BotBlockerMiddleware

Aplicado globalmente pelo Kernel — você não precisa adicioná-lo nas rotas. Bloqueia automaticamente ferramentas de ataque conhecidas (sqlmap, nikto, nmap, burpsuite, etc.) pelo User-Agent, requisições de API sem User-Agent, e IPs com score de ameaça acumulado alto. Em desenvolvimento local, o bloqueio por score é desativado para não atrapalhar os testes.

Ferramentas bloqueadas automaticamente

sqlmap, nikto, nmap, masscan, nuclei, dirbuster, gobuster, wfuzz, ffuf, feroxbuster, hydra, burpsuite, acunetix, nessus, openvas, scrapy, libwww-perl e outras. O bloqueio gera log em JSON no stderr para integração com Fail2Ban.

HttpsEnforcerMiddleware

Aplicado globalmente pelo Kernel quando COOKIE_SECURE=true no .env. Bloqueia requisições HTTP com 403 e sugere o link HTTPS equivalente. Retorna JSON para chamadas de API e página HTML para browsers.

bash
# Ativa no .env para forçar HTTPS em produção
COOKIE_SECURE=true
COOKIE_HTTPONLY=true

SecurityHeadersMiddleware

Aplicado globalmente pelo Kernel. Garante que todos os headers de segurança estejam presentes em toda resposta: Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, Referrer-Policy e outros. Você não precisa configurar nada — funciona automaticamente.

Combinações recomendadas

Caso de usoMiddlewares
Rota pública com rate limit[RateLimitMiddleware]
Rota pública com proteção de banco[RateLimitMiddleware, CircuitBreakerMiddleware]
Rota privada (qualquer usuário logado)[AuthHybridMiddleware]
Rota privada com rate limit[RateLimitMiddleware, AuthHybridMiddleware]
Rota admin[AuthHybridMiddleware, AdminOnlyMiddleware]
Rota admin com rate limit e circuit breaker[RateLimitMiddleware, CircuitBreakerMiddleware, AuthHybridMiddleware, AdminOnlyMiddleware]
Integração machine-to-machine[ApiTokenMiddleware]
Conteúdo diferente para logados/não logados[OptionalAuthHybridMiddleware]
Página HTML protegida (dashboard)[AuthPageMiddleware]
Exemplo completo — módulo com todas as proteções
php
use Src\Kernel\Middlewares\AuthHybridMiddleware;
use Src\Kernel\Middlewares\AdminOnlyMiddleware;
use Src\Kernel\Middlewares\RateLimitMiddleware;
use Src\Kernel\Middlewares\CircuitBreakerMiddleware;

/** @var \Src\Kernel\Contracts\RouterInterface $router */

$admin     = [AuthHybridMiddleware::class, AdminOnlyMiddleware::class];
$user      = [AuthHybridMiddleware::class];
$dbCircuit = [CircuitBreakerMiddleware::class, ['service' => 'database', 'threshold' => 5, 'cooldown' => 20]];
$pubLimit  = [RateLimitMiddleware::class, ['limit' => 30, 'window' => 60, 'key' => 'produto.publico']];
$crtLimit  = [RateLimitMiddleware::class, ['limit' => 10, 'window' => 60, 'key' => 'produto.criar']];

// Público com rate limit e circuit breaker
$router->get('/api/produtos',      [ProdutoController::class, 'listar'], [$pubLimit, $dbCircuit]);
$router->get('/api/produtos/{id}', [ProdutoController::class, 'buscar'], [$pubLimit, $dbCircuit]);

// Usuário autenticado
$router->get('/api/meus-produtos', [ProdutoController::class, 'meus'], $user);

// Admin com rate limit e circuit breaker
$router->post('/api/admin/produtos',        [ProdutoController::class, 'criar'],    [$crtLimit, $dbCircuit, ...$admin]);
$router->put('/api/admin/produtos/{id}',    [ProdutoController::class, 'atualizar'], [...$admin, $dbCircuit]);
$router->delete('/api/admin/produtos/{id}', [ProdutoController::class, 'deletar'],   $admin);