Os problemas de cross-origin no desenvolvimento front-end surgem devido à política de mesma origem dos navegadores, que impede o acesso a recursos de origens diferentes por razões de segurança. Essa limitação impacta diretamente requisições AJAX, bloqueando o acesso a recursos cross-origin por padrão.
Existem diversas técnicas para contornar essa restrição, incluindo JSONP, document.domain com iframes, location.hash com iframes, window.name com iframes, postMessage, CORS (Cross-Origin Resource Sharing), proxies via Node.js, WebSockets e proxies com nginx.
Utilizando JSONP para Comunicação Cross-Origin
JSONP permite a comunicação cross-origin ao explorar a capacidade de carregar scripts de diferentes domínios. A abordagem envolve a criação dinâmica de elementos script que executam uma função de retorno definida pelo cliente.
Exemplo com JavaScript puro:
<script>
var elementoScript = document.createElement('script');
elementoScript.type = 'text/javascript';
elementoScript.src = 'https://api.servidor.com/dados?callback=processarDados';
document.head.appendChild(elementoScript);
function processarDados(info) {
console.log('Dados recebidos:', info);
}
</script>
Com jQuery:
$.ajax({
url: 'https://api.servidor.com/dados',
type: 'GET',
dataType: 'jsonp',
jsonpCallback: 'processarDados',
data: { consulta: 'valor' }
});
Limitação: JSONP suporta apenas requisições GET.
Empregando document.domain com iframes
Esta técnica é aplicável quando domínios compartilham o mesmo domínio principal, mas diferem em subdomínios. Ambas as páginas devem configurar document.domain para o domínio principle comum.
Exemplo no domínio pai (http://www.exemplo.com/pagina-principle.html):
<iframe id="frameFilho" src="http://sub.exemplo.com/pagina-filha.html"></iframe>
<script>
document.domain = 'exemplo.com';
var dadosCompartilhados = 'valorConfidencial';
</script>
Exemplo no domínio filho (http://sub.exemplo.com/pagina-filha.html):
<script>
document.domain = 'exemplo.com';
console.log('Dados do pai:', window.parent.dadosCompartilhados);
</script>
Comunicação Cross-Origin via location.hash e iframes
Este método utiliza iframes encadeados para transmitir dados através do fragmento da URL (hash). Uma página intermediária no mesmo domínio que a origem facilita a comunicação.
Implementação envolve três páginas: uma no domínio A (origem), uma no domínio B (destino intermediário) e outra no domínio A (intermediário para retorno).
Exemplo simplificado no domínio A:
<iframe id="frameIntermediario" src="https://outro-dominio.com/pagina-intermediaria.html" style="display:none;"></iframe>
<script>
var frame = document.getElementById('frameIntermediario');
// Envia dados via hash após carregamento
frame.onload = function() {
frame.src += '#dados=teste';
};
// Função para receber dados de retorno
function receberDados(info) {
alert('Dados recebidos: ' + info);
}
</script>
Utilizando window.name para Transferência de Dados
A propriedade window.name mantém seu valor mesmo após navegações entre domínios, permitindo a trensferência de dados de até 2MB. Um iframe é usado para carregar a página cross-origin e, em seguida, redirecionado para uma página no mesmo domínio para ler os dados.
Exemplo:
function obterDadosCrossOrigin(urlDestino, callback) {
var etapa = 0;
var frame = document.createElement('iframe');
frame.src = urlDestino;
frame.onload = function() {
if (etapa === 1) {
callback(frame.contentWindow.name);
frame.parentNode.removeChild(frame);
} else if (etapa === 0) {
frame.contentWindow.location = 'https://meu-dominio.com/pagina-proxy.html';
etapa = 1;
}
};
document.body.appendChild(frame);
}
// Uso
obterDadosCrossOrigin('https://outro-dominio.com/dados.html', function(dados) {
console.log('Dados obtidos:', dados);
});
Comunicação Cross-Origin com postMessage
A API postMessage do HTML5 permite a troca segura de mensagens entre janelas, iframes ou pop-ups de diferentes origens.
Exemplo de envio e recebimento de mensagens:
// No frame que envia a mensagem
var frameDestino = document.getElementById('frameExterno');
var mensagem = { acao: 'solicitar', conteudo: 'valor' };
frameDestino.contentWindow.postMessage(JSON.stringify(mensagem), 'https://outro-dominio.com');
// No frame que recebe a mensagem
window.addEventListener('message', function(evento) {
if (evento.origin === 'https://meu-dominio.com') {
var dadosRecebidos = JSON.parse(evento.data);
console.log('Mensagem recebida:', dadosRecebidos);
// Responder pode ser feito com evento.source.postMessage(...)
}
}, false);
Implementando CORS (Cross-Origin Resource Sharing)
CORS é um mecanismo padrão que permite que servidores especifiquem quais origens podem acessar seus recursos. Requer configuração no servidor, e no cliente para requisições com credenciais.
Exemplo com XMLHttpRequest:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true; // Inclui cookies na requisição
xhr.open('GET', 'https://api.externa.com/recursos', true);
xhr.onload = function() {
if (xhr.status === 200) {
console.log('Resposta:', xhr.responseText);
}
};
xhr.send();
Configuração no servidor (exemplo em Node.js com Express):
app.use(function(requisicao, resposta, proximo) {
resposta.header('Access-Control-Allow-Origin', 'https://meu-dominio.com');
resposta.header('Access-Control-Allow-Credentials', 'true');
resposta.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
proximo();
});
Proxy Cross-Origin com Node.js
Um servidor proxy em Node.js atua como intermediário, encaminhando requisições do cliente para o servidor de destino, evitando restrições cross-origin no navegador.
Exemplo usando Express e http-proxy-middleware:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use('/api', createProxyMiddleware({
target: 'https://servidor-alvo.com',
changeOrigin: true,
onProxyRes: function(proxyRes, req, res) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
}));
app.listen(3000);
Utilizando WebSockets para Comunicação Cross-Origin
WebSockets permitem comunicação bidirecional em tempo real entre cliente e servidor, e são isentos da política de mesma origem.
Exemplo com Socket.IO:
<script src="https://cdn.socket.io/4.5.0/socket.io.min.js"></script>
<script>
var socket = io('https://servidor-websocket.com');
socket.on('connect', function() {
socket.emit('evento', { dado: 'valor' });
socket.on('resposta', function(msg) {
console.log('Mensagem do servidor:', msg);
});
});
</script>
Proxy Cross-Origin com Nginx
Nginx pode ser configurado como proxy reverso para encaminhar requisições cross-origin, lidando com CORS e reescrita de cookies.
Exemplo de configuração no nginx:
server {
listen 80;
server_name meu-dominio.com;
location /api/ {
proxy_pass https://servidor-externo.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
}
}
Isso permite que o frontend faça requisições para /api/ no mesmo domínio, enquanto o nginx redireciona para o servidor externo, resolvendo os problemas de cross-origin.