什么是跨域
首先我们要知道所谓不同域就是协议、域名、端口这三者其中一个不一样。比如说:
- http://www.a.com 与 https://www.a.com 不同域,因为协议不同
- http://m.a.com 与 http://www.a.com 不同域,因为域名不同(子域名不同)
- http://www.a.com 与 http://www. b.net 不同域,因为域名不同(主域名不同)
- http://www.a.com:80 与 http://www.a.com:443 不同域,因为端口不同
- http://www.a.com 与 http://www.a.com/login 同域,因为除了请求资源地址不同以外,其他都相同
而跨域自然就是指某域下请求另一个域的资源。Javascript为了安全,提出了同源策略,限制了跨域。同源策略最早是Netscape为了cookie而创建的规则,它要求某些操作只能在同域下进行。受到同源策略影响的是下面这三类:
- 获取cookie、localstorage、IndexDB。
- DOM操作
- Ajax请求
前两点很容易理解,如果可以跨域获取localstorage岂不是很容易就能盗取信息?如果能跨域操作DOM岂不是能插入广告或者获取密码框的值?
至于Ajax请求为什么不能跨域,我们可以想象一下这个场景:我的电脑能同时访问内网和网外,有一天我点开了一个钓鱼网站,如果没有同源策略的话钓鱼网站就能发起一个跨域Ajax请求,通过我获取到内网的信息。因此我们需要限制跨域Ajax请求。
解决跨域问题的方法
在实际开发中我们经常需要发起一些跨域请求,这就需要我们想办法规避同源策略的限制。解决跨域问题的方法有很多,我这里介绍最常见的几种方法。
jsonp跨域
jsonp利用了<script>
标签可以跨域的特点,我们可以在a域下请求一个b域的脚本文件,而这个脚本文件里面就带上了我们需要的信息。
|
|
通过这个例子可以看出,jsonp最大的缺点就是不支持post请求。
另外,jQuery等都能很方便地实现jsonp,大家可以查阅一下相关api。
跨域资源共享(CROS)
这个方法非常简单,在不涉及cookie的情况下只需要服务端设置一个Access-Control-Allow-Origin
的响应头。如果需要带上cookie,则前端需要设置xhr.withCredentials = true;
。
CROS跨域与jsonp相比,他可以发起get和post请求,在信息量大的时候尤其有优势。目前这个方法已经得到主流现代浏览器的支持,没有意外的话会成为将来的主流。
关于Access-Control-Allow-Origin
需要补充两点:
- 他的参数只能是
*
或者是指定的域名, - 有时候我们需要接收主域名为
a.com
的所有域名的请求,类似实现*.baidu.com
这种。我们可以通过后台判断请求头的origin,判断之后动态的往Access-Control-Allow-Origin
里写入这个域名。而无需手动将所有域名一个个添加进Access-Control-Allow-Origin
。
location.hash + iframe
主要是利用了两个特性:
- 父窗口和子窗口可以相互操作对方的location.hash
- hash的变化不会引起页面重新加载
这个方法的过程大概是:
- 让子窗口在b域下,父窗口在a域下
- 父窗口修改子窗口的hash值,通过hash值将请求信息传给子窗口
- 子窗口坚挺到hash变化之后,在b域发起请求
- 子窗口拿到返回之后,修改父窗口的hash值,通过hash值将返回信息传回给父窗口
- 父窗口监听到hash变化之后获取hash携带的返回信息并作出相应
下面简单展示一个例子,为了简单起见省掉了父窗口修改子窗口的hash值这一步,直接在加载iframe的时候就发起请求。
|
|
|
|
window.name + iframe
这里利用了window.name的两个特性:
- 切换到不同页面不同域后,window.name依然存在
- 可以给window.name设置很大的值,(2MB)
利用这两个特性我们可以实现跨域,步骤如下:
- 先将子窗口切换到b域,
- 子窗口在b域请求之后把数据写在window.name中,再切换回a域。
- a域下的父窗口通过
.contentWindow.name
获取数据
|
|
|
|
postMessage
window.postMessage这是HTML5 XMLHttpRequest Level 2中的API,允许跨窗口通信,无论两个窗口是不是在同一个域。
他有三个参数:
- message,需要发送的数据,字符串类型。必选
- targetOrigin,来指定哪些窗口能接收到消息事件,可以是
*
也可以是URL。必选 - transfer,是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。可选
另外需要注意的是接收方必须要监听message事件,否则会报错。
|
|
|
|
其他
除了上面提到过的五种跨域方法以外,还有一些其他方法:
- nginx代理跨域
- Nodejs中间件代理跨域
- WebSocket协议跨域
这些方法涉及了Nginx、NodeJs等知识,我暂时还搞不定,就不赘述了。
先挖个坑,日后再补。— 2018.2.9