« 回到主页

同源策略与跨域问题

1 同源策略

出于安全方面的考虑,为了保证用户信息的安全,防止恶意的网站盗取数据,不允许跨域调用其他页面的对象。
同源:

域名一域名二是否允许通信
http://www.domain.com/a.htmlhttp://www.domain.com/b.html同一域名,允许通信
http://www.domain.com/main/a.htmlhttp://www.domain.com/rest/b.html同一域名,不同文件夹,允许通信
http://www.domain.com:8000/a.htmlhttp://www.domain.com/b.html同一域名,不同端口,不允许通信
http://www.domain.com/a.htmlhttps://www.domain.com/b.html同一域名,不同协议,不允许通信
http://www.domain.com/a.htmlhttp://10.3.8.211/b.html域名和域名对应IP 不允许通信
http://www.domain.com/a.htmlhttp://suzy.domain.com/b.html相同主域,不同二级域名,不允许通信
http://www.domain.com/a.htmlhttp://domain.com/b.html相同主域,不同二级域名,不允许通信
http://www.domain.com/a.htmlhttp://www.suzy.com/b.html不同域名,不允许通信

注意:

2 非同源时受限的三种行为

3 跨域的解决方案

3.1 跨域获取cookie

一级域名相同,只是二级域名不同的情况下,浏览器允许通过设置document.domain共享Cookie。也就是说,Cookie只能跨二级域名来访问,不能跨一级域名来访问。

3.2 跨域获取localStorage

假设有http://javascript.exam.com/text.html和http://jquery.exam.com/text.html两个页面。
通过http://javascript.exam.com/text.html页面去修改http://jquery.exam.com/text.html页面的本地数据:

3.3 跨域获取DOM

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

3.3.1 如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain属性,即可跨域获取DOM

http://blogs.msnova.net/b.html:

document.domain = "msnova.net";

http://www.msnova.net/a.html:

document.domain = "msnova.net";
var ifr = document.createElement('iframe');
ifr.src = 'http://blogs.msnova.net/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
    var x = ifr.contentDocument;
    alert(x.getElementsByTagName("h1")[0].childNodes[0].nodeValue); //操作b.html
    ifr.onload = null;
}

3.3.2 否则,可以通过window.name和window.postMessage跨域获取DOM

3.3.2.1 通过window.name跨域获取DOM

在iframe指向的页面写入window.name,在本页面通过iframe的contentWindow.name获取。
http://JavaScript.exam.com/text.html:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <script>
      window.name = "value";
    </script>
  </body>
</html>

http://catagory.exam.com/text.html:

<!DOCTYPE html>
<html>
  <head>
`</head>
  <body>
    <iframe id="iframe" onload="loading()" src="http://javascript.exam.com/text.html"></iframe>
    <script>
      var load = false;
      function loading() {
        if (load == false) {
          // 同域处理,请求后会再次重新加载iframe
          document.getElementById('iframe').contentWindow.location = 'http://catagory.exam.com/index.html';
          load = true;
        }
        else {
          // 获取window.name的内容,注意必须进行同域处理后方可访问!
          alert(document.getElementById('iframe').contentWindow.name); //输出:value
          load = false;
        }
      }
    </script>
  </body>
</html>

3.3.2.2 通过window.postMessage跨域获取DOM

在本页面写入iframe的contentWindow.postMessage,在iframe指向的页面监听message事件获取。
http://catagory.exam.com/text.html:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <iframe id="iframe" src="http://JavaScript.exam.com/Test/text.html"></iframe>
    <script>
      window.onload = function() {
        document.getElementById('iframe').contentWindow.postMessage('Hello', "http://JavaScript.exam.com");
      };
    </script>
  </body>
</html>

http://JavaScript.exam.com/text.html:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
    <script>
      window.addEventListener('message', function(event){
        // 通过origin属性判断消息来源地址
        if (event.origin == 'http://catagory.exam.com')
          alert(event.data); //输出:Hello
      }, false);
    </script>
  </body>
</html>

3.4 跨域获取Ajax请求

Ajax跨域的解决方案有2种:CORS、JSONP。

3.4.1 CORS

CrossOrigin Resource Sharing跨域资源共享。
当前几乎所有的浏览器(Internet Explorer 8+,Firefox 3.5+,Safari 4+和Chrome)都可通过名为跨域资源共享(Cross-Origin Resource Sharing)的协议支持ajax跨域调用。
对一个简单的请求,没有自定义头部,要么使用GET,要么使用POST,它的主体是text/plain,请求用一个名叫Origin的额外的头部发送。Origin头部包含请求页面的头部(协议,域名,端口),这样服务器可以很容易的决定它是否应该提供响应。
服务器端:JSP页面中设置response.addHeader(“Access-Control-Allow-Origin”, “http://www.yoursite.com:8080”)。
在请求信息中,浏览器使用 Origin 这个 HTTP 头来标识该请求来自于 http://www.yoursite.com:8080(发出跨区请求的url)。
在返回的响应信息中,使用 Access-Control-Allow-Origin 头来控制哪些域名的脚本可以访问该资源。
如果设置 Access-Control-Allow-Origin为*,则允许所有域名的脚本访问该资源。如果有多个,则只需要使用逗号分隔开即可。
如果设置Access-Control-Allow-Credentials响应头为true,则允许在CORS中携带Cookie。

3.4.2 JSONP

通过callback形式实现跨域访问。JSONP比JSON外面有多了一层callback(),也就是说,在服务器端需要先将查询结果转换成JSON格式,然后用参数callback在JSON外面再套一层,就变成了JSONP。

3.4.2.1 JavaScript与JSONP

JSONP的简单实现模式:考虑到script标签的src属性可以跨域,动态创建一个script标签,在src中传递要发送的数据和回调函数,服务器接收数据处理后生成JSON数据,用回调函数包裹后,返回给客户端,完成回调。

3.4.2.2 jQuery与JSONP

jQuery框架也支持JSONP,可以使用$.getJSON(url,[data],[callback])方法和$.ajax()方法。

3.4.2.2.1 getJSON()方法

getJSON()方法在url的后面必须添加一个callback参数,这样getJSON()方法才知道是用JSONP方式,callback后面的问号是内部自动生成的一个回调函数名。

3.4.2.2.2 $.ajax()方法

使用$.ajax形式可以不在url的后面添加一个callback参数,但此时需要指定jsonp参数。jsonp: “callback”代表的是服务端通过String callback = request.getParameter(“callback”) 接收客户端回调函数名的参数名,ajax请求中jsonp参数的默认值就是callback,也可以自己随便定义。jsonpCallback: “callbackHandler”代表的是服务端调用结束后的本地回调函数名,比如jsonp: “callback”中的客户端回调函数名,jsonpCallback的参数值也可以自己随便定义,也可以不给jsonpCallback参数,其实jQuery会自动生成一个函数和函数名,远程服务调用成功后,既执行了success这个回调函数,也执行自己定义的jsonpCallback指定的回调函数,所以完全可以使用jQuery生成的回调函数,在调用结束后在success回调中做相应的处理即可。

« 回到主页