【问题标题】:Why do some cross-domain JSON-requests fail but others don't?为什么有些跨域 JSON 请求会失败,而有些则不会?
【发布时间】:2011-05-12 07:36:41
【问题描述】:

我在理解 JSON 的安全性方面有点困难,因为通常在理论上不应该工作的事情,似乎可以。 AFAIK,来自位于域 A 上的页面上的脚本的调用,不应该能够从域 B 接收数据。但是在下面的代码中,对一个外部域的调用失败,而另一个通过。两者都不是打包的 JSON 调用 (jsonp)。

这是为什么?不应该禁止两者都通过浏览器安全检查吗?我在 Chrome 和 Firefox 中得到相同的结果。如果我在 dropbox.com 上托管以下 html 页面,Chrome 会给我以下错误消息:

XMLHttpRequest 无法加载 http://www.odinfond.no/rest/fund/calc/fundReturn?&id=300&oneTimeInvestment=100000&oneTimeInvestmentDate=2009-11-01&endDate=2010-11-01&currency=NOK。 原点http://dl.dropbox.com 不是 允许 访问控制允许来源。

单击this direct link 可以看到如果呼叫通过我会得到的 JSON 响应。对其他服务的调用成功返回。我在 Dropbox 上托管以下代码。 Try it out here.

<!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>

【问题讨论】:

    标签: javascript jquery json same-origin-policy


    【解决方案1】:

    你会注意到工作请求有一个响应头:

    Access-Control-Allow-Origin: *
    

    这是释放浏览器以使脚本可以使用响应的原因。 (请注意,请求总是发出,同源策略仅影响脚本是否可以访问响应)

    如果 '*' 是主机名,则仅当当前文档的主机名与 Access-Control-Allow-Origin 标头匹配时才允许访问

    【讨论】:

    • 谢谢,这很有意义!我认为请求几乎相同,但我从未注意到响应标头中的差异:-)
    【解决方案2】:

    浏览source code,似乎 $.ajax() 检测到远程 URL 并替换 AJAX (XMLHttpRequest ) 带有良好的旧脚本标签:

        // 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;
        }
    

    【讨论】:

    • 该 sn-p 的第一行表明只有在您将 {dataType:"script"} 传递给 AJAX 调用时才会发生这种情况
    • 我添加了之前的摘录,其中s.dataType 的值发生了变化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 2017-09-17
    • 2013-04-23
    • 2021-01-23
    相关资源
    最近更新 更多