因为在开发过程中会经常遇到因为浏览器同源策略而导致的跨域问题,而多数开发者对浏览器同源策略和跨域问题并没有很清晰的认识,所以打算在这篇文章中说下浏览器同源策略和我们最经常会遇到的 Ajax 跨域问题及其解决方案。
对于源的定义,MDN 中是这么解释的:如果两个页面的协议、域名和端口都相同,则两个页面具有相同的源。
从定义我们可以知道,关注两个页面是否同源,只要比较两个页面的协议、域名和端口即可。
举个例子,假设有以下页面,比较 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) 。
在浏览器中,一个最核心也最基本的安全功能便是同源策略。
同源策略是指浏览器中一个源的脚本只能访问同源的另一个脚本的策略。
也就是说,在浏览器中的脚本如果要访问其它脚本的话,那么两个脚本必须是同源的,否则会受到浏览器同源策略的限制。如果两个脚本非同源,会有三个行为受到限制
- DOM 无法获得;
- Cookie、LocalStorage 和 IndexDB 无法共享;
- Ajax 请求限制;
DOM 无法获得
DOM 无法获得的限制最常见的是在 iframe 窗口与父窗口之间,如果父窗口与其 iframe 窗口的脚本是不同源的,则它们互相无法获取对方的 DOM 元素。
如下父窗口与 iframe 不同源,父窗口中无法获取 iframe 窗口中脚本的 DOM 元素
上述代码打印出来的效果如下所示
同理,在 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 是解决跨域很常用的一种方法了,其原理是通过