Definição de Origem Cruzada
O conceito de cross-origin surge quando um documento web tenta acessar recursos hospedados em uma origem distinta, seja por diferença no protocolo, domínio ou porta. Por motivos de segurança, os navegadores modernos bloqueiam essas requisições por padrão, fundamentados na Política de Mesma Origem (Same-Origin Policy). Esta机制 visa impedir interações maliciosas entre recursos de fontes não confiáveis.
Regras da Política de Mesma Origem
Para que duas origens sejam consideradas iguais, elas devem compartilhar exatamente os seguintes atributos:
- Protocolo: Ambos devem utilizar o mesmo esquema (ex: HTTP ou HTTPS).
- Domínio: O hostname deve ser idêntico (ex: muedominio.com).
- Porta: O número da porta deve coincidir (ex: 80, 443 ou 3000).
Caso qualquer um desses três elementos difira, o navegador classifica a interação como跨域 (cross-origin) e aplica as restrições de segurança.
Cenários Comuns de Violação
Requisições跨域 são iniciadas pelo cliente (browser) quando há tentativa de comunicação com um endpoint externo. Abaixo estão exemplos práticos de situações que acionam essas restrições:
1. Domínios Distintos
// Página atual: https://loja.virtual.com
fetch('https://servicos.terceiros.net/v1/dados'); // Bloqueado por domínio diferente
2. Subdomínios Diferentes
// Página atual: https://app.loja.virtual.com
fetch('https://api.loja.virtual.com/recursos'); // Bloqueado por subdomínio diferente
3. Protocolos Incompatíveis
// Página atual: https://secure.site.com
fetch('http://insecure.site.com/info'); // Bloqueado por HTTP vs HTTPS
4. Portas Divergentes
// Página atual: http://localhost:3000
fetch('http://localhost:8080/api'); // Bloqueado por porta diferente
Estratégias para Contornar Restrições
Existem diversas abordagens técnicas para permitir comunicação legítima entre origens diferentes, dependendo dos requisitos do projeto.
1. Técnica JSONP (JSON with Padding)
Método legado que explora a permissão de execução de scripts externos. Funciona apenas para requisições GET, inserindo dinamicamente uma tag script no DOM.
<script>
function callbackDados(payload) {
console.log('Dados recebidos:', payload);
}
const tagScript = document.createElement('script');
tagScript.src = 'https://api.legada.com/info?fn=callbackDados';
document.body.appendChild(tagScript);
</script>
2. Padrão CORS (Cross-Origin Resource Sharing)
Solução moderna baseada em cabeçalhos HTTP. O servidor deve explicitamente autorizar origens específicas através de headers de resposta.
Access-Control-Allow-Origin: https://app.cliente.com.br
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Em ambientes Node.js com Express, isso pode ser configurado via middleware:
const express = require('express');
const app = express();
const middlewareCors = (req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'https://app.cliente.com.br');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
next();
};
app.use(middlewareCors);
app.get('/recurso', (req, res) => {
res.json({ status: 'Acesso permitido via CORS' });
});
app.listen(4000, () => {
console.log('Servidor ativo na porta 4000');
});
3. Proxy Reverso
Configurar um intermediário no mesmo domínio do front-end que repassa as requisições ao back-end real, eliminando a diferença de origem para o navegador.
const { createProxyMiddleware } = require('http-proxy-middleware');
const configProxy = createProxyMiddleware({
target: 'https://backend.real.com',
changeOrigin: true,
});
app.use('/gateway', configProxy);
4. Comunicação via Iframe e postMessage
Permite troca de mensagens segura entre janelas de origens diferentes embutidas na página.
<!-- Página Pai -->
<iframe id="frameExterno" src="https://dominio-externo.com"></iframe>
<script>
const frame = document.getElementById('frameExterno');
window.addEventListener('message', (evento) => {
console.log('Mensagem do iframe:', evento.data);
});
frame.contentWindow.postMessage('Solicitação segura', 'https://dominio-externo.com');
</script>
Configuração CORS no Spring Framework
Aplicações Java baseadas em Spring seguem a política de mesma origem por padrão. É necessário configurar explicitamente o compartilhamento de recursos.
Configuração Global via WebMvcConfigurer
Implemente a interface de configuração para aplicar regras a toda a aplicação.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ConfiguracaoWeb implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registro) {
registro.addMapping("/**")
.allowedOrigins("https://dominio-autorizado.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
Configuração via Anotação @CrossOrigin
Permite definir permissões diretamente no controller ou em métodos específicos.
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = "https://app.cliente.com.br")
@RestController
public class ControladorDados {
@GetMapping("/endpoint/seguro")
public String obterInformacao() {
return "Dados acessíveis via CORS configurado";
}
}
Implementação via Filtro Servlet
Para controle granular, um filtro personalizado pode manipular diretamente a resposta HTTP.
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class FiltroAcessoCors implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}