Para resolver esse problema, o mecanismo CORS (Compartilhamento de Recursos de Origem Cruzada) foi desenvolvido. Este artigo explorará em detalhes as causas dos problemas de cross-domain, o funcionamento do mecanismo CORS e as soluções comuns para lidar com esses desafios em desenvolvimento real.
- O que é Cross-Domain?
Cross-domain refere-se à situação em que, no navegador, o script de uma página web tenta acessar recursos de outro domínio (com protocolo, domínio e porta diferentes), e o navegador bloqueia essa requisição. Para garantir a segurança do usuário, os navegadores utilizam a política de mesma origem, que permite apenas requisições de mesma origem.
- Mesma origem: refere-se a protocolo, domínio e porta idênticos.
- Cross-domain: se protocolo, domínio ou porta forem diferentes, é considerada uma requisição cross-domain.
Por exemplo, se a página frontend estiver em http://localhost:3000 e o serviço API em http://api.exemplo.com, a requisição do frontend para a API do backend será uma requisição cross-domain.
- Por que precisamos de CORS?
Para compensar as limitações da política de mesma origem, o mecanismo CORS (Cross-Origin Resource Sharing) foi desenvolvido. É um protocolo entre navegador e servidor usado para permitir requisições cros-domain. Quando um navegador faz uma requisição cross-domain, o servidor adiciona cabeçalhos específicos de CORS na resposta, indicando quais requisições são permitidas, permitindo assim o compartilhamento de recursos entre origens diferentes.
- Detalhes do mecanismo CORS
O funcionamento do CORS baseia-se em cabeçalhos específicos em requisições e respostas HTTP. O navegador adiciona automaticamente alguns cabeçalhos ao fazer requisições cross-domain, enquanto o servidor precisa retornar cabeçalhos de permissão cross-domain com base nesses cabeçalhos. CORS tem principalmente dois tipos de requisições: requisições simples e requisições não simples, sendo que as requisições não simples exigem uma requisição de verificação prévia (Preflight) para confirmar se a operação cross-domain é permitida.
3.1 Requisições simples e requisições de verificação prévia
- Requisição simples: são consideradas requisições que atendem a condições específicas, como usar métodos GET ou POST, e cabeçalhos de requisição limitados a Accept, Content-Type, etc.
- Requisição não simples: se a requisição não atender às condições de requisição simples (como usar métodos PUT, DELETE, ou incluir cabeçalhos personalizados), o navegador primeiro anviará uma requisição de verificação prévia. Esta é uma requisição OPTIONS que pergunta ao servidor se a requisição cross-domain real é permitida.
- Soluções comuns para problemas de cross-domain
Existem várias maneiras de resolver problemas de cross-domain. Abaixo estão algumas soluções comuns.
4.1 Usando cabeçalhos CORS (solução mais comum)
A configuração de cabeçalhos CORS no lado do servidor é a solução mais comum para cross-domain. Ao adicionar cabeçalhos CORS específicos nos cabeçalhos de resposta HTTP do servidor, o servidor pode informar explicitamente ao navegador quais requisições cross-domain são permitidas.
Exemplo: Node.js (Express)
No Node.js, geralmente usamos o middleware cors para configurar cross-domain. Aqui está um exemplo de configuração simples:
const servidor = require('express');
const configuracaoCross = require('cors');
const aplicacao = servidor();
// Configuração CORS
const opcoesCors = {
origemPermitida: 'http://exemplo.com', // Origem permitida
metodosPermitidos: ['GET', 'POST'], // Métodos HTTP permitidos
cabecalhosPermitidos: ['Content-Type', 'Authorization'], // Cabeçalhos de requisição permitidos
credenciais: true // Permitir credenciais
};
aplicacao.use(configuracaoCross(opcoesCors));
aplicacao.get('/dados', (requisicao, resposta) => {
resposta.json({ mensagem: 'Requisição cross-domain bem-sucedida' });
});
aplicacao.listen(3000, () => {
console.log('Servidor iniciado');
});
Neste exemplo:
origemPermitidaespecifica o domínio de origem permitido para requisições cross-domain.metodosPermitidosdefine os métodos HTTP permitidos.cabecalhosPermitidoslista os cabeçalhos de requisição permitidos.credenciaisdefinido comotruepermite que requisições cross-domain carreguem Cookies ou informações de autenticação.
Exemplo: Java (Spring Boot)
No Spring Boot, podemos usar a anotação @CrossOrigin para suporte a cross-domain:
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ControladorDados {
@CrossOrigin(origins = "http://exemplo.com", allowedHeaders = "*", allowCredentials = "true")
@GetMapping("/dados")
public String obterDados() {
return "Requisição cross-domain bem-sucedida";
}
}
A anotação @CrossOrigin permite configurar configurações de cross-domain para interfaces ou classes específicas. Podemos especificar os domínios permitidos (origins), cabeçalhos de requisição permitidos (allowedHeaders), e se requisições cross-domain podem carregar credenciais (allowCredentials).
4.2 Servidor Proxy
Outra solução comum para problemas de cross-domain é usar um servidor proxy. A requisição do frontend primeiro é enviada para um servidor proxy de mesma origem do frontend, que então encaminha a requisição para o servidor de destino, contornando assim as limitações de cross-domain do navegador. Ferramentas de construção frontend como Webpack oferecem funcionalidades de proxy durante o desenvolvimento.
Exemplo: Proxy Node.js
const http = require('http');
const proxy = require('http-proxy');
// Criar servidor proxy
const servidorProxy = proxy.createProxyServer({});
// Criar servidor simples para encaminhar requisições
http.createServer((requisicao, resposta) => {
servidorProxy.web(requisicao, resposta, { destino: 'http://exemplo.com' });
}).listen(8000);
Neste exemplo, todas as requisições são enviadas primeiro para http://localhost:8000, e então o servidor proxy encaminha a requisição para http://exemplo.com, permitindo assim requisições cross-domain.
4.3 JSONP (apenas para requisições GET)
JSONP (JSON com Preenchimento) é uma técnica que implementa cross-domain inserindo dinamicamente tags <script>. Como as tags <script> não têm limitações de cross-domain, podemos usar essa característica para contornar a política de mesma origem. Embora o JSONP tenha sido muito popular no passado, devido à sua limitação de suportar apenas requisições GET e a certos riscos de segurança, não é mais recomendado em desenvolvimento web moderno.
4.4 Usando WebSocket
WebSocket é um protocolo de comunicação bidirecional que não é limitado pela política de mesma origem, podendo ser usado para comunicação cross-domain. WebSocket é especialmante adequado para cenários que exigem comunicação em tempo real, como chats instantâneos, jogos online e outras aplicações.
Exemplo: Cliente WebSocket
const socket = new WebSocket('ws://exemplo.com/socket');
// Escutar conexão estabelecida
socket.onopen = function(evento) {
console.log('WebSocket conectado');
socket.send('Olá Servidor');
};
// Escutar recebimento de mensagens
socket.onmessage = function(evento) {
console.log('Mensagem recebida:', evento.data);
};
Com WebSocket, o cliente pode estabelecer uma conexão persistente com o servidor, evitando problemas de cross-domain.