浏览器同源策略及 Ajax 跨域解决方案

由于在开发过程当中会常常遇到由于浏览器同源策略而致使的跨域问题,而多数开发者对浏览器同源策略和跨域问题并无很清晰的认识,因此打算在这篇文章中说下浏览器同源策略和咱们最常常会遇到的 Ajax 跨域问题及其解决方案。javascript

对于源的定义,MDN 中是这么解释的:若是两个页面的协议、域名和端口都相同,则两个页面具备相同的源。html

从定义咱们能够知道,关注两个页面是否同源,只要比较两个页面的协议域名端口便可。java

举个例子,假设有如下页面,比较 A 页面与其它页面是否同源~跨域

A:http://xys.ttsy/a.html 
B:http://xys.ttsy/b.html 
C:https://xys.ttsy/c.html 
D:http://d.xys.ttsy/d.html 
E:http://xys.ttsy:8081/e.html 
复制代码

根据定义,能够知道 A 和 B 同源,而 A 和 C、D、E 不一样源。A、B 页面同源是由于其协议(都是 http)、域名(都是 xys.ttsy)和端口(都是 80)都相同;而 A 与 C、D、E 不一样源,是由于 A 和 C 不一样协议(http 和 https),A 和 D 不一样域名(xys.ttsy 和 d.xys.ttsy),A 和 E 不一样端口(80 和 8081) 。浏览器

在浏览器中,一个最核心也最基本的安全功能即是同源策略。安全

同源策略是指浏览器中一个源的脚本只能访问同源的另外一个脚本的策略。bash

也就是说,在浏览器中的脚本若是要访问其它脚本的话,那么两个脚本必须是同源的,不然会受到浏览器同源策略的限制。若是两个脚本非同源,会有三个行为受到限制服务器

  • DOM 没法得到;
  • Cookie、LocalStorage 和 IndexDB 没法共享;
  • Ajax 请求限制;

DOM 没法得到

DOM 没法得到的限制最多见的是在 iframe 窗口与父窗口之间,若是父窗口与其 iframe 窗口的脚本是不一样源的,则它们互相没法获取对方的 DOM 元素。app

以下父窗口与 iframe 不一样源,父窗口中没法获取 iframe 窗口中脚本的 DOM 元素dom

<iframe id="myIframe" src="http://www.taobao.com" width="500" height="500" ></iframe>
<script type="text/javascript">
    var myIframe = document.getElementById('myIframe').contentWindow.document;
    console.log(myIframe)  // 能获取到 document 对象,但里面没有有效数据
    console.log(myIframe.body)  // 空的 body
</script>
复制代码

上述代码打印出来的效果以下所示

无效的 document 和 body

同理,在 iframe 窗口脚本中也没法获取父窗口脚本的 DOM 元素

console.log(self.parent.document)
console.log(self.top.document)
console.log(self.parent.document.body)
console.log(self.top.document.body)
复制代码

那么,有没有什么方式可以规避此类同源策略的限制呢?答案是有的,只是这其中也是有条件的。

当父窗口与 iframe 窗口一级域名相同而二级域名不一样的时候(或者说有共同的一级域名更为准确一点),则能够经过设置 document.domain 属性来规避此类同源策略的限制。

只要分别在两个窗口中对应的脚本文件中设置以下代码便可

document.domain='ttsy.com';  // 设置同一个一级域名
复制代码

Cookie、LocalStorage 和 IndexDB 没法共享

若两个脚本不一样源,则 Cookie、LocalStorage 和 IndexDB 的内容没法共享。

对于 Cookie 来讲,有两种方式能够规避此类同源策略的限制。

如果一级域名相同而二级域名不一样的状况(或者说有共同的一级域名更为准确一点),则能够经过设置 document.domain 属性来规避此类同源策略的限制。

只要两个脚本文件设置相同的 document.domain 值,便可共享 Cookie 。

document.domain='ttsy';  // 设置相同的值
复制代码

第二种方式则是服务器端代码在设置 Cookie 的时候,将 domian 属性设置为一级域名,那么该一级域名下的子域名一样能够共享 Cookie 。

而 LocalStorage 和 IndexDB 则没法经过上述方法来规避同源策略。

而对于彻底不一样源的页面来讲,还能够经过 window.name、window.postMessage 等方式来实现通信,本篇不继续赘述,有兴趣的童鞋能够查阅相关资料了解。

Ajax 请求限制

在一个脚本中,若是经过 Ajax 请求另外一个非同源的脚本,则会报错。报错信息常常会相似下面酱紫

XMLHttpRequest cannot load xxx  . No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
复制代码

这便是咱们常常提到的 Ajax 跨域问题,这是因为浏览器的同源策略引发的,Ajax 没法请求非同源的资源。

对于 Ajax 跨域解决方案,一般来讲有以下三种:

  • JSONP
  • CORS
  • 代理服务器

下面就详细描述上述三种 Ajax 跨域解决方案。

JSONP

JSONP 是解决跨域很经常使用的一种方法了,其原理是经过 script 标签向服务器端发起请求不受浏览器同源策略的限制。而服务器端在接受到请求后,能够返回一个指定名字的函数,该函数的参数是须要返回的数据。

举个例子吶~

var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = 'http://ttsy.com/getName?callback=fn';
document.body.appendChild(script);
    
function fn(data) {
    console.log('name: ' + data.name);
};
复制代码

上述代码经过建立一个 script 标签,将其 src 属性设置为执行请求的 url 并将其插入到页面中,向服务器发起一个请求。上述代码中请求的 url 为 http://ttsy.com/getName?callback=fn ,指定了回调函数名为 fn 。

而服务端在收到该请求后返回一个指定名字的函数,该函数的参数是须要返回的数据。服务端返回数据以下

fn({
        "name": "ttsy"
    });
复制代码

因为经过 script 标签请求到的数据会直接执行,因此上述服务端返回数据后会直接执行 fn 函数,因此在上述代码中 fn 函数将会输出 name:ttsy 。

基于 JSONP 的实现原理,其只能经过 get 请求来得到数据,不能进行较为复杂的 post 请求,因此在更多的状况下,咱们会采用下面的 CORS 来解决 Ajax 的跨域问题。

CORS

CORS(Cross-origin resource sharing),全称是跨域资源共享。它容许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。是 Ajax 跨域问题的根本解决方案。

CORS 的实现须要浏览器与服务器共同支持。

目前来讲,基本全部的浏览器都支持 CORS 功能,当浏览器发现 Ajax 跨域请求时,会在 HTTP 请求头添加一些附加的头信息,有时会多出一次 HTTP 请求,这些都是由浏览器自动完成的。

而在服务器中要实现了 CORS 功能,则须要设置 Access-Control-Allow-Origin 属性。

Access-Control-Allow-Origin: *
复制代码

代理服务器

代理服务器是指经过设置一个同源的代理服务器,而后将咱们的 Ajax 请求发送到咱们的代理服务器上,再经过代理服务器去向实际的服务器去请求数据,最后经过代理服务器返回给浏览器。

经过设置代理服务器来规避浏览器同源策略的限制,其原理是服务器之间的资源请求并无同源策略的限制。可是因为操做起来并不方便,因此在实际开发中并不会常常用这种方法来解决问题。

以为还不错的小伙伴,能够关注一波公众号哦。

相关文章
相关标签/搜索