Instalação Completa do Ambiente React Native
Projeto: Federal-Pro
Crie a pasta Federal-Pro e entre no diretório antes de inicializar a estrutura.
Gere a estrutura inicial apontando para a versão SDK 57 do Expo no diretório corrente.
Instale as bibliotecas de navegação e componentes para ajuste de tela e status bar no Android.
Suporte nativo para gerenciar e carregar fontes personalizadas do Google no dispositivo Android/iOS.
Pacotes para suporte nativo a FontAwesome 5 (via vector-icons) e Google Material Symbols (via SVG).
Instalação dos mecanismos de estilo (classes utilitárias ou CSS-in-JS).
Banco de dados local (AsyncStorage) e cliente HTTP (Axios) para consumo de APIs.
Inicie o servidor Metro Bundler. Pressione a no terminal para abrir no emulador Android ou leia o QR Code com o app Expo Go no celular.
Instalação da biblioteca no Composer, criação da tabela de blacklist MySQLi e arquivo completo do backend (jwt_system.php) para tratamento de login, refresh token, revogação e middleware global de proteção.
1. Instalação da dependência PHP via Composer:
2. Tabela MySQLi para Blacklist e Revogação:
CREATE TABLE IF NOT EXISTS `jwt_blacklist` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `token_hash` VARCHAR(32) NOT NULL UNIQUE, `data_expiracao` DATETIME NOT NULL, `criado_em` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX (`token_hash`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. Código do backend PHP unificado (jwt_system.php):
<?php
/**
* SISTEMA COMPLETO DE AUTENTICAÇÃO JWT
* Arquivo unificado com suporte a Login, Geração, Validação, Refresh Token e Middleware Global.
*/
require_once __DIR__ . '/vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
// 1. CONFIGURAÇÕES GERAIS E CONEXÃO COM O BANCO
define('JWT_SECRET', 'SUA_CHAVE_SECRETA_EXTREMAMENTE_SEGURA_E_LONGA_12345');
define('JWT_ALGO', 'HS256');
define('JWT_EXPIRATION', 3600); // 1 hora de validade para o access token
define('JWT_REFRESH_EXPIRATION', 86400 * 7); // 7 dias para o refresh token
// Conexão MySQLi
$conn = new mysqli("localhost", "valorp3497_adm", "vp@140698", "valorp3497_adm");
if ($conn->connect_error) {
die(json_encode(['status' => 'erro', 'mensagem' => 'Falha na conexão com o banco de dados.']));
}
// 2. FUNÇÕES AUXILIARES DE JWT E SEGURANÇA
function gerarTokensJWT($id_usuario, $email_usuario) {
$agora = time();
$ip_cliente = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'desconhecido';
$payloadAccess = [
'iat' => $agora,
'exp' => $agora + JWT_EXPIRATION,
'uid' => $id_usuario,
'email' => $email_usuario,
'ip' => $ip_cliente,
'ua' => md5($user_agent),
'tipo' => 'access'
];
$payloadRefresh = [
'iat' => $agora,
'exp' => $agora + JWT_REFRESH_EXPIRATION,
'uid' => $id_usuario,
'tipo' => 'refresh'
];
return [
'access_token' => JWT::encode($payloadAccess, JWT_SECRET, JWT_ALGO),
'refresh_token' => JWT::encode($payloadRefresh, JWT_SECRET, JWT_ALGO)
];
}
function validarJWT($token, $tipoEsperado = 'access') {
global $conn;
try {
$decoded = JWT::decode($token, JWT_SECRET, [new Key(JWT_SECRET, JWT_ALGO)]);
$dados = (array) $decoded;
if (($dados['tipo'] ?? '') !== $tipoEsperado) {
return ['valido' => false, 'erro' => 'Tipo de token inválido.'];
}
if ($tipoEsperado === 'access') {
$ua_atual = md5($_SERVER['HTTP_USER_AGENT'] ?? 'desconhecido');
if ($dados['ua'] !== $ua_atual) {
return ['valido' => false, 'erro' => 'Ambiente de acesso alterado.'];
}
}
$stmt = $conn->prepare("SELECT id FROM jwt_blacklist WHERE token_hash = ?");
$hash = md5($token);
$stmt->bind_param("s", $hash);
$stmt->execute();
if ($stmt->get_result()->num_rows > 0) {
return ['valido' => false, 'erro' => 'Token revogado.'];
}
return ['valido' => true, 'dados' => $dados];
} catch (ExpiredException $e) {
return ['valido' => false, 'erro' => 'Token expirado.'];
} catch (SignatureInvalidException $e) {
return ['valido' => false, 'erro' => 'Assinatura de token inválida.'];
} catch (Exception $e) {
return ['valido' => false, 'erro' => 'Token inválido.'];
}
}
function definirCookieJWT($nome, $token, $tempoExpiracao) {
setcookie($nome, $token, [
'expires' => time() + $tempoExpiracao,
'path' => '/',
'secure' => isset($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Lax'
]);
}
function revogarJWT($token) {
global $conn;
$hash = md5($token);
$expiracao = time() + JWT_REFRESH_EXPIRATION;
$stmt = $conn->prepare("INSERT IGNORE INTO jwt_blacklist (token_hash, data_expiracao) VALUES (?, FROM_UNIXTIME(?))");
$stmt->bind_param("si", $hash, $expiracao);
$stmt->execute();
}
// 3. PROCESSAMENTO DAS AÇÕES DO SISTEMA (LOGIN, REFRESH, LOGOUT)
$acao = $_GET['action'] ?? $_POST['action'] ?? null;
if ($acao === 'login') {
header('Content-Type: application/json');
$email = trim($_POST['email'] ?? '');
$senha = trim($_POST['senha'] ?? '');
if (empty($email) || empty($senha)) {
http_response_code(400);
echo json_encode(['status' => 'erro', 'mensagem' => 'Preencha todos os campos.']);
exit;
}
$stmt = $conn->prepare("SELECT id, email, senha FROM usuarios WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$res = $stmt->get_result();
if ($user = $res->fetch_assoc()) {
if (password_verify($senha, $user['senha'])) {
$tokens = gerarTokensJWT($user['id'], $user['email']);
definirCookieJWT('auth_token', $tokens['access_token'], JWT_EXPIRATION);
definirCookieJWT('refresh_token', $tokens['refresh_token'], JWT_REFRESH_EXPIRATION);
echo json_encode([
'status' => 'sucesso',
'mensagem' => 'Login realizado com sucesso.',
'access_token' => $tokens['access_token'],
'refresh_token' => $tokens['refresh_token']
]);
exit;
}
}
http_response_code(401);
echo json_encode(['status' => 'erro', 'mensagem' => 'Credenciais incorretas.']);
exit;
}
if ($acao === 'refresh') {
header('Content-Type: application/json');
$refreshToken = $_COOKIE['refresh_token'] ?? $_POST['refresh_token'] ?? null;
if (!$refreshToken) {
http_response_code(400);
echo json_encode(['status' => 'erro', 'mensagem' => 'Refresh token não fornecido.']);
exit;
}
$validacao = validarJWT($refreshToken, 'refresh');
if ($validacao['valido']) {
$uid = $validacao['dados']['uid'];
$stmt = $conn->prepare("SELECT id, email FROM usuarios WHERE id = ?");
$stmt->bind_param("i", $uid);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();
if ($user) {
$novosTokens = gerarTokensJWT($user['id'], $user['email']);
revogarJWT($refreshToken);
definirCookieJWT('auth_token', $novosTokens['access_token'], JWT_EXPIRATION);
definirCookieJWT('refresh_token', $novosTokens['refresh_token'], JWT_REFRESH_EXPIRATION);
echo json_encode([
'status' => 'sucesso',
'access_token' => $novosTokens['access_token'],
'refresh_token' => $novosTokens['refresh_token']
]);
exit;
}
}
http_response_code(401);
echo json_encode(['status' => 'erro', 'mensagem' => 'Refresh token inválido ou expirado.']);
exit;
}
if ($acao === 'logout') {
$token = $_COOKIE['auth_token'] ?? null;
$refreshToken = $_COOKIE['refresh_token'] ?? null;
if ($token) revogarJWT($token);
if ($refreshToken) revogarJWT($refreshToken);
setcookie('auth_token', '', time() - 3600, '/');
setcookie('refresh_token', '', time() - 3600, '/');
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
header('Content-Type: application/json');
echo json_encode(['status' => 'sucesso', 'mensagem' => 'Logout efetuado.']);
exit;
}
header('Location: login.php');
exit;
}
// 4. MIDDLEWARE GLOBAL DE PROTEÇÃO DO SISTEMA
$token = null;
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
$token = $matches[1];
}
if (!$token && isset($_COOKIE['auth_token'])) {
$token = $_COOKIE['auth_token'];
}
$checagem = $token ? validarJWT($token, 'access') : ['valido' => false, 'erro' => 'Token ausente.'];
if (!$checagem['valido']) {
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
header('Content-Type: application/json');
http_response_code(401);
echo json_encode([
'status' => 'erro',
'mensagem' => $checagem['erro'],
'exige_refresh' => ($checagem['erro'] === 'Token expirado.')
]);
exit;
}
header('Location: login.php');
exit;
}
$usuario_logado_id = $checagem['dados']['uid'];
$usuario_logado_email = $checagem['dados']['email'];