再谈跨域

老话题常谈,不过对内部一些原理做一些释义,也解一些自己的惑。

流程在手,跨域不愁。

1. jsonp

a. 当前页面 www.bbeeii.com window下 有一个dosomething方法,用于处理获取的数据处理
b. 当前页面 www.bbeeii.com append一个script标签,src指向 www.baidu.com?callback=dosomething&xxx=xxx
c. 要通信的页面 www.baidu.com 基于xxx 或者不基于xxx 处理出一些数据,并进行json_encode(PHP将数据转换成json格式的方法)
d. 要通信的页面 www.baidu.com 返回 dosomething(data)
e. 当前页面 www.bbeeii.com append进来的script请求成功,dosomething(data)代码自动执行
f. 一个jsonp请求发起、服务端处理、服务端返回、前端处理完成

注意:
a. 要通信的页面 www.baidu.com 返回的数据格式需要是 dosomething(data) 这样的形式,因为他是直接放到页面里执行的。
b. 要通信的页面 www.baidu.com 返回的数据的Content-Type 最好是 application/javascript
c. 因为 application/javascript 有时会被运营商劫持,因此(实际)也可以采用 application/json

2. document.domain / iframe

注意:
a. 因为同源策略,当前页面 www.bbeeii.com 下放入 iframe(src为kuaidi100.com)在不同域下,当前页面的脚本不可以访问iframe里的内容,除了location属性(location.href也不行)。
b. 当当前页面和iframe在同一源下,当前页面就可以和iframe进行各种操作。比如:当前页面获取iframe中的元素、访问其window下的变量;iframe访问当前页面的元素、window下的变量…
c. 可以通过修改document.domain=bbeeii.com 来让当前页面 www.bbeeii.com 和 iframe d.bbeeii.com在同一个源下

关于document.domain (reference)
a. 主域名必须相同(不能将当前页面 www.bbeeii.com 的domain设置成 baidu.com)
b. 当前页面、iframe 都需要设置成相同的domain,才能被认为是同一个“域”

不要觉得,我设置一个document.domain就可以完成一个跨域了。之前我也这么觉得的,后来才发现这里的水还是有点深。

应用场景:需要完成一个a.example.com 与 b.example.com 的跨域ajax请求

具体操作流程:(示例)
a. 当前页面 a.example.com 创建一个iframe,src为b.example.com/proxy.html
b. 当前页面 a.example.com 与嵌套的iframe页面 b.example.com/proxy.html 都设置document.domain为 example.com
c. 此时,同源策略不再限制,也就使得 a.example.com 可以访问 b.example.com/proxy.html 下的元素、变量等等
d. 因为iframe页面b.example.com/proxy.html 与具体要请求的页面b.example.com 是在同一个b.example.com域下,ajax也就不会有跨域问题因此才可以通过控制iframe b.example.com/proxy.html 下的jQuery(或者其他、或者原生XMLHttpRequest)向b.example.com发起一个ajax请求
e. 通过iframe页面b.example.com/proxy.html 发起的请求,经过b.example.com处理返回数据后,再通过iframe页面可以访问当前页面a.example.com的特性,将数据传递给a.example.com做处理。
d. 自此完成a.example.com 传送数据,经由b.example.com/proxy.html发起的ajax请求到b.example.com,b.example.com处理返回数据,经由b.example.com/proxy.html 将数据返回给a.example.com 处理的流程

附:
为什么设置a.example.com b.example.com domain都是example.com还是不能进行ajax请求
简答:因为document.domain允许的是客户端到客户端的访问,而不是客户端到服务端的访问,例如:a.example.com 设置了domain为example.com,向b.example.com发起了一个ajax请求,请求是落在服务器的,还没有到达客户端,此时的b.example.com域名还是b.example.com,被认为是跨域。因此需要设置Access-Control-Allow-Origin

3. 服务器proxy

还是同源策略,通过iframe也不能访问不同源下的dom元素,window下的变量。
例如:www.bbeeii.com 想要展示kuaidi100.com的页面内容,只能iframe。但是不能在kuaidi100.com页面基础上做其他更进一步的操作(除了location)。但是想要去除kuaidi100.com页面上的广告…

示例:
a. www.bbeeii.com 向 example.com 发起一个正常的ajax、或者jsonp、或者iframe跨子域请求。
b. example.com 服务器上接收传递来的url数据,抓取 kuaidi100.com的页面内容,进行一次处理然后返回
c. www.bbeeii.com 拿到对应的数据,进行展示。

优点:可以完成任何跨域资源请求
缺点:经过了中间服务器这一层,请求效率相对比较低

4. HTML5 CORS Ajax跨域

update 2015.09.02 —— 之前经历了这样一个场景,m.bbeeii.com.cn下的一个A页面使用一些信息,通过jsonp的请求方式,请求www.bbeeii.com进行登录( www.bbeeii.com 设置session cookie)。而后,A页面继续发送跨域请求给 api.bbeeii.com.cn的一个POST接口,发现请求返回结果是未登录(因为是 www.bbeeii.com 下的接口做的“登录”,session cookie 被设置到 bbeeii.com下)。同理,即使将页面A迁移到m.bbeeii.com 也无用,还需要将接口也迁移到 api.bbeeii.com。类似这种 需要登录态的POST请求,需要注意。