You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
另外,再移除节点的时候,需要注意 head 里是否有 base 节点的情况,如果存在 base 节点 并且是自闭合的写法的话,最好把 script 插在 head.firstChild 位置,或者移除节点的时候通过 script.parentNode.removeChild(script) 来完成,否则 IE6 会报错。 via
后端解决
前端代码不做修改,还是以 ajax 形式从同域服务器获取数据,后端同学从 server 端去获取数据,然后返回给前端标准的 JSON 数据,这个情况是没有域这个概念。
聊聊跨域
这篇文章是 2013 年在搜狐视频时为分享的一个 Topic 写的,主要从两个场景:获取跨域服务器相关资源和纯客户端页面和页面之间的跨域信息获取 以及在这两个场景下都有哪些技术方案可以选择。
因为 2014 年底和近期又遇到过两次和跨域有关的 Case,而且还是同一个, 所以就把这个 Case 当做一个知识点补充进来。
不存在完美的方案,只有适合特定场景的方案。
什么是跨域?
简单说就是因为 javascript 的同源策略限制,当域名不同时,安全考虑则禁止了彼此间的一个通信。
什么情况下会造成跨域?
协议不同,端口不同,子域不同,都会造成跨域
什么情况下需要跨域?
前端交互开发中,往往需要往不同于当前域名的后端服务器获取/提交一些数据,或者是不同 window 之间的一个通信,但因为同源限制,导致 javascript 开发者不得不采取一些措施去解决这一技术问题,以下分别就这两个场景做一些技术总结。
与服务器的交互通信
JSONP
因为 DOM 中可以插入第三方 javascript 文件并执行,所以利用这一特性,服务器在返回的数据上 wrap 一层函数名,以 javascript 函数调用的形式返回给客户端。
优点:
缺点:
需要注意的一个地方,其本质是通过往 head 里插入一个 script 节点,来让外域的 js 执行达到跨域这样一个目的,所以如果是一个频繁来获取数据的情况下,需要再该 js 节点执行完之后,移除该节点。
移除该节点的同时,其占用的内存事实上没有释放的,还需要删除一下该节点上的属性。一个最佳实践是最好创建一个 script 节点,然后去一次次改变它的 src 属性。via
另外,再移除节点的时候,需要注意 head 里是否有 base 节点的情况,如果存在 base 节点 并且是自闭合的写法的话,最好把 script 插在 head.firstChild 位置,或者移除节点的时候通过
script.parentNode.removeChild(script)
来完成,否则 IE6 会报错。 via后端解决
前端代码不做修改,还是以 ajax 形式从同域服务器获取数据,后端同学从 server 端去获取数据,然后返回给前端标准的 JSON 数据,这个情况是没有域这个概念。
优点:
缺点:
服务器配置
举个栗子
Nginx
Apache
生产环境的话,就需要根据各种不同的情况来进行配置,同时增加一些 header 的配置。
优点:
缺点:
XHR 2
HTML5 新增的特性,只要被请求的目标服务器配置 header
就可以了。这个的值写法比较奇葩,必须写 HTTP,还不支持通配符。
优点:
缺点
2015-04-26 更新
如果在一个不需要支持低版本浏览器的环境下,这个方案目前来看非常完善,但是有一个关于缓存的很重要的问题,需要注意:
问题现状是这样,在客户端缓存了一个需要跨域获取的资源时,因为第一次加载需要和服务器交互,服务器返回了跨域头,一切都正常,符合预期。但是在该资源未过期的前提下,当用户通过敲回车的形式加载页面再次请求该文件时,客户端不会向服务器发请求,直接返回200(from cache)的信息,但又因为该资源是需要跨域头的,现在本地的缓存是没有这个头,所以会直接报出跨域错误的信息。这个一定要注意。(文章开头提到的两个 Case 发生的原因就是这个,一个case 是因为用 Ajax 来获取的 js 资源,结果缓存后再通过 Ajax 获取发现是大量的 status 0,跨域错误的,导致页面出不来;另一个是 ajax 获取数据。)
补充的一点,ie虽说不支持标准,但勉为其难得ie8, 9支持
XDomainRequest
对象,权且当做对开发者的一点安慰吧。link最后再补充一个小点,关于客户端在获取一个跨域资源时是否会和服务器端进行一次交互的问题,答案是会。客户端当识别这是个跨域资源时,会验证服务器的Response Header 中是否有关于跨域头的设置,如果没有就抛出跨域错误,如果有则资源正常加载。
页面间的跨域通信
从域名的角度来分两种情况, 跨子域,跨主域。
从使用场景来分,父窗口和子 iframe 之间的通信,以及 tab 和 tab 之间的通信。tab 和 tab 之间的通信有一个前置条件为 其中一个 tab 必须由前一个 tab 打开,也就是依赖一个句柄引用。
关于 iframe location 的读写限制,与导航相关属性的操作限制如下:
那关于 iframe 想读父窗口的 location.href 怎么办
top.location.href
来读跨子域
在两个需要通信的页面上设置
优点
缺点, 太多了
结论:先知其弊而避之;再知其弊而用之。
location.hash + iframe
背景是这样:父页面 parent.com/p.html 和子页面 child.com/c.html 通信,c.html 需要向 p.html 传递数据。
思路: 此时需要在子页面中添加一个和 p.html 同域的 iframe 代理页面 meditor.html,数据通过该代理 iframe 的 location hash 进行传递。
优点
缺点
对于数据量比较大的情况,可以通过建立多个 iframe 来进行传递,举个栗子,
c.html
要传递abcdef
长度为6的字符串,假设浏览器每次对 iframe 携带数据长度限制为2,怎么办呢?既要保证数据没有遗漏,又要保证在数据被拆分后,又能按照原始位置进行拼接完好?也就是说 每个 iframe 都需要携带三部分数据: 1. 数据总长度; 2. 当前数据片要插入的位置;3. 数据分片.
所以,分解后的数据传递应该是这个样子
之后装载数据便可。
window.name + iframe
当对一个页面设置window.name
后,即使改变该页面 url,window.name
也不会被重写。所以借助这一特点,可以实现跨域的数据传递。// 2014-04-26 更新
关于 window.name 的一点补充
改写顶层页面 window.name 值后,通过改变页面 url,对于后续页面
另外一种情况,当设置子 iframe 中 window.name,当改变 iframe 的 location 后
window.name 不会因为改变页面 url (包括域不同)而恢复为初始值。当然了,如果父窗口想读取该值,那需要子 iframe 和父为同源。
这里,便是借助第二种情况这个特点,来实现跨域的数据传递。
背景没变,父页面
parent.com/p.html
和子页面child.com/c.html
通信,c.html 需要向p.html传递数据。原理大概是这样:在 c.html 中设置window.name的值 ,值为需要传递给
p.html
的数据。设置完毕,p.html 重写 iframe.src, 使新的 src 指向和 p.html 为同一个域名的一个空白代理页, 当 iframe 加载完毕,读取该iframe.contentWindow.name
(这个时候已经是同域了).优点
postMessage
HTML5 新增的一个技术,IE 的支持情况呢 IE 分两个阶段 IE8+ 和 IE10+ 来讲。
No such interface supported
对于第一点的不足,这里 其实也是给出了一些实现上的方法
另外说说 firefox
flash
需要在网站根目录或相关目录下放置一个 crossdomain.xml 文件
具体配置可以在网上搜搜
优点:
缺点:
一些topic
跨域 post
两种情况
如果并不很在意跨域 post 提交结果的返回值,(比如,在能保证网络链接正常和程序无 bug的情况下,post 提交都会返回一个正常值。) 这个情况可以简单用
setTimeout
来搞如果 post 提交会出现很多种情况,这种情况下,需要借助服务器端的跳转来完成。大致可以这样去解决:增加中转页面
a.com/meditor.html
, a.com 的页面提交数据通过 iframe 提交至 b.com 服务端后,服务端处理完后通过跳转到a.com/meditor.html
并在中转页面的 location.hash 上附带服务器端处理结果,此时中转页就可以通过调用 parent 的函数来完成业务逻辑。img 和 script 的 crossorigin
对 img 标签来说,增加了该属性,可以允许其他地方使用该图片,这样描述不太准确,想象一下 canvas.drawImage 可以根据一个图片来绘制,就是这个。当为第三方域的图片时,如果不加该属性,虽然说可以 drawImage 出来,但是当后续想访问该 canvas 的一些 function 时,例如我调用
toDataURL
,toBlob
,getImageData
, 就会抛出一个SecurityError的异常也是为了避免未经授权的图片信息不正当使用。
对于 script 标签来说,添加该属性后,可以将该脚本的一些信息暴露出来,比如 onerror 事件, 捕捉该事件时,同域的话,可以拿到相关信息,比如 message,line,type, 但跨域默认是拿不到的,只会抛出一个
Script error.
解决办法: 加了这个属性就可以拿到了。 当然了,需要搭配服务器返回
Access-Control-Allow-Origin: *
的 headercss 是否加载完成的判断
老版本的 firefox 中,当判断跨域的 css 文件是否加载完成用到的一个 error 事件,原理是当 css 加载完成并尝试访问
node.sheet.cssRules
时,会抛出NS_ERROR_DOM_SECURITY_ERR
的这么一个 error,基于此便可以确定该不同域的 css 是否加载完成.参考
The text was updated successfully, but these errors were encountered: