Entendendo o Conceito de Cross-Origin
No desenvolvimento web, a segurança do navegador é regida pela Política de Mesma Origem (Same-Origin Policy - SOP). Uma "origem" é definida pela combinação de quatro elementos fundamentais de uma URL:
- Protocolo: (ex: http, https)
- Subdomínio: (ex: www, api)
- Domínio Principal: (ex: site.com)
- Porta: (ex: 80, 8080, 443)
Considere a URL: http://api.sistema.com.br:9000/v1/usuarios. Se qualquer um dos componentes acima (protocolo, subdomínio, domínio ou porta) for diferente entre a página que faz a requisição e o servidor que recebe, ocorre o que chamamos de Cross-Origin. Por padrão, o JavaScript não tem permissão para ler dados de origens diferentes devido a restrições de segurança, a menos que técnicas específicas sejam implementadas.
Principais Estratégias de Solução
1. Proxy reverso
O método de proxy consiste em utilizar o próprio servidor web da aplicação para atuar como intermediário. Em vez de o navegador tentar acessar dominio-b.com diretamente, ele faz uma requisição para o seu próprio servidor (dominio-a.com/api-proxy), que por sua vez solicita os dados ao destino final e os devolve ao cliente. Como a comunicação entre servidores não sofre restrições de SOP, o problema é resolvido no backend.
2. JSONP (JSON com Padding)
O JSONP explora uma exceção na regra de mesma origem: a tag <script> pode carregar arquivos de qualquer domínio. O cliente define uma função de callback e o servidor retorna os dados encapsulados em uma chamada dessa função.
Limitação importante: O JSONP suporta apenas requisições do tipo GET.
3. CORS / XHR2 (XMLHttpRequest Level 2)
O Cross-Origin Resource Sharing (CORS) é o padrão moderno. Ele utiliza cabeçalhos HTTP para permitir que o servidor informe ao navegador quais origens têm permissão para acessar seus recursos. É suportado por todos os navegadores modernos (IE10+).
Implementação Prática: JSONP
Abaixo, um exemplo de interface para consulta e cadastro de colaboradores utilizando jQuery com suporte a JSONP para a busca.
<!-- Interface do Cliente -->
<div>
<h2>Painel de Colaboradores</h2>
<fieldset>
<legend>Busca de Funcionário</legend>
<input type="text" id="idBusca" placeholder="Código do funcionário" />
<button id="btnConsultar">Pesquisar</button>
<div id="outputBusca"></div>
</fieldset>
<fieldset>
<legend>Cadastro</legend>
<input type="text" id="nomeColaborador" placeholder="Nome" /><br/>
<input type="text" id="codColaborador" placeholder="Código" /><br/>
<select id="generoColaborador">
<option value="M">Masculino</option>
<option value="F">Feminino</option>
</select><br/>
<button id="btnSalvar">Registrar</button>
<div id="outputCadastro"></div>
</fieldset>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
// Consulta via JSONP
$("#btnConsultar").on("click", function() {
$.ajax({
type: "GET",
url: "http://api-remota.test/servico_jsonp.php",
data: { id: $("#idBusca").val() },
dataType: "jsonp",
jsonp: "callback",
success: function(res) {
if(res.status === "ok") {
$("#outputBusca").html("<strong>Resultado:</strong> " + res.info);
} else {
$("#outputBusca").html("Erro: " + res.info);
}
},
error: function(err) {
console.error("Erro na requisição: " + err.status);
}
});
});
// Cadastro via POST (Exige configuração de CORS no servidor)
$("#btnSalvar").on("click", function() {
$.ajax({
type: "POST",
url: "http://api-remota.test/servico_jsonp.php",
dataType: "json",
data: {
nome: $("#nomeColaborador").val(),
codigo: $("#codColaborador").val(),
sexo: $("#generoColaborador").val()
},
success: function(res) {
$("#outputCadastro").text(res.info);
}
});
});
});
</script>
No backend (PHP), o script deve capturar o parâmetro de callback e retornar o JavaScript executável:
<?php
header("Content-Type: application/json; charset=utf-8");
$repositorio = [
["id" => "001", "nome" => "Carlos Silva", "cargo" => "Analista"],
["id" => "002", "nome" => "Ana Souza", "cargo" => "Designer"]
];
$metodo = $_SERVER["REQUEST_METHOD"];
if ($metodo === "GET") {
$cb = $_GET["callback"];
$idRequisitado = $_GET["id"];
$encontrado = null;
foreach ($repositorio as $item) {
if ($item["id"] == $idRequisitado) {
$encontrado = $item;
break;
}
}
if ($encontrado) {
$resposta = json_encode(["status" => "ok", "info" => "Nome: " . $encontrado["nome"]]);
} else {
$resposta = json_encode(["status" => "erro", "info" => "Não localizado"]);
}
// Saída específica para JSONP
echo $cb . "(" . $resposta . ");";
} elseif ($metodo === "POST") {
// Lógica de salvamento
echo json_encode(["status" => "ok", "info" => "Cadastro realizado com sucesso"]);
}
?>
Implementação com CORS (XHR2)
Para permitir que o navegador aceite a resposta de um domínio diferente sem usar JSONP, o servidor deve enviar cabeçalhos específicos. Esta é a abordagem recomendada para aplicações modernas, permitindo o uso de GET, POST, PUT e DELETE.
No topo do arquivo PHP (ex: servico_cors.php), adicione:
<?php
// Permite qualquer origem (em produção, substitua * pelo domínio específico)
header("Access-Control-Allow-Origin: *");
// Define os métodos permitidos
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
// Define o tipo de conteúdo
header("Content-Type: application/json; charset=utf-8");
// Resto da lógica do serviço...
?>
Ao utilizar CORS, o jQuery não precisa de dataType: "jsonp". Você pode utilizar dataType: "json" normalmente, e o navegador cuidará da validação dos cabeçalhos de segurança de forma transparente.