Por que algumas solicitações JSON de domínio cruzado falham, mas outros não?
-
26-09-2019 - |
Pergunta
Tenho um pouco de problemas para entender a segurança em JSON, porque muitas vezes as coisas que, em teoria não deveriam funcionar, aparentemente funcionam. Afaik, chamadas de um script em uma página que reside no domínio A, não devem poder receber dados de um domínio B. Mas no código abaixo das chamadas para um domínio externo falham, enquanto outro passa. E nenhum deles é ligue para JSON (JSONP).
Por que é isso? Ambos não devem ser exibidos por passar pelas verificações de segurança do navegador? Eu recebo os mesmos resultados em Chrome e Firefox. Se eu hospedar o HTML-página abaixo no DropBox.com, o Chrome me dará esta mensagem de erro:
XmlHttPrequest não pode carregar http://www.odinfond.no/rest/fund/calc/fundreturn?&id=300&onetimeinvestment=100000&oneTimeinvestmentdate=2009-11-01&enddate=2010-11-01äcy=nok. Origem http://dl.dropbox.com não é permitido pela Origin-Origin Access-Control.
A resposta JSON que eu teria recebido se a chamada passada pudesse ser vista clicando Este link direto. A chamada para o outro serviço retorna com sucesso. Eu hospedo o código abaixo no Dropbox. Experimente aqui.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>JSON/JSONP test</title>
<script src="jquery.js" type="text/javascript"></script>
</head>
<body>
<script>
service = 'http://www.odinfond.no/rest/fund/calc/fundReturn?';
parameters = {
id: '300',
oneTimeInvestment:'100000',
oneTimeInvestmentDate:'2009-11-01',
endDate:'2010-11-01',
currency:'NOK'
}
$.getJSON( service, parameters, function(data) {
alert("Success");
});
service = 'http://ws.geonames.org/postalCodeLookupJSON?'
parameters = {
postalcode:1540,
country:'NO'
}
$.getJSON(service, parameters, function(data) {
alert(data.postalcodes[0].adminName2);
});
</script>
<p>Use Firebug to see JSON response</p>
</body>
</html>
Solução
Você notará que o pedido de trabalho tem um cabeçalho de resposta:
Access-Control-Allow-Origin: *
É isso que libera o navegador para disponibilizar a resposta ao script. (Observe que a solicitação é sempre Feito, a mesma política de origem afeta apenas se a resposta é acessível ao script ou não)
Se o '*' for um nome de host, o acesso só será permitido se o nome do host do documento atual corresponder ao Access-Control-Allow-Origin
cabeçalho
Outras dicas
Navegando no Código fonte, parece que $ .ajax () detecta URLs remotos e substitui o AJAX (xmlHttPrequest) por boas etiquetas de script antigas:
// Build temporary JSONP function
if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
jsonp = s.jsonpCallback || ("jsonp" + jsc++);
// Replace the =? sequence both in the query string and the data
if ( s.data ) {
s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
}
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
// We need to make sure
// that a JSONP style response is executed properly
s.dataType = "script";
// Handle JSONP-style loading
var customJsonp = window[ jsonp ];
window[ jsonp ] = function( tmp ) {
if ( jQuery.isFunction( customJsonp ) ) {
customJsonp( tmp );
} else {
// Garbage collect
window[ jsonp ] = undefined;
try {
delete window[ jsonp ];
} catch( jsonpError ) {}
}
data = tmp;
jQuery.handleSuccess( s, xhr, status, data );
jQuery.handleComplete( s, xhr, status, data );
if ( head ) {
head.removeChild( script );
}
};
}
[...]
// Matches an absolute URL, and saves the domain
var parts = rurl.exec( s.url ),
remote = parts && (parts[1] && parts[1].toLowerCase() !== location.protocol || parts[2].toLowerCase() !== location.host);
// If we're requesting a remote document
// and trying to load JSON or Script with a GET
if ( s.dataType === "script" && type === "GET" && remote ) {
var head = document.getElementsByTagName("head")[0] || document.documentElement;
var script = document.createElement("script");
if ( s.scriptCharset ) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Handle Script loading
if ( !jsonp ) {
var done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function() {
if ( !done && (!this.readyState ||
this.readyState === "loaded" || this.readyState === "complete") ) {
done = true;
jQuery.handleSuccess( s, xhr, status, data );
jQuery.handleComplete( s, xhr, status, data );
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
if ( head && script.parentNode ) {
head.removeChild( script );
}
}
};
}
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore( script, head.firstChild );
// We handle everything using the script element injection
return undefined;
}