跨域解决方案

在工作过程中遇到了跨域问题,参考了IBM一篇文章,写了一篇读后感,其实是按照自己的理解把原文几乎翻译了一遍,希望对大家有所帮助,如果有问题欢迎大家指正~原文地址: https://www.ibm.com/developerworks/library/wa-crossdomaincomm/

出于安全考虑,浏览器有一些限制,在请求url资源的时候,域名、端口、应用协议,只要有一个和当前页面不同就认为不同源,也就不允许访问,我们叫它sop(same origin policy)同源策略。栗子:现在有2个不同源的页面A和页面B。

A页面可以做的事:
1、从页面B获取css、js、图片文件
2、包含一个资源指向页面B的iframe/frame元素
3、通过HTML元素的src属性,向页面B传递一些信息,比如ifram或img

A页面不能做的事:
1、发起一个请求B页面的ajax请求
2、获得或操作指向B页面的iframe/frame的内容

之所以增加这个限制,就是防止在不同的网页互相交换数据时,保护用户不受有害的攻击。

有很多解决方案,例如:jsonp证明一个网页可以动态从其他的源加载脚本,但是jsonp有2个主要的限制:1、没有错误处理机制 2、必须用get方法,这就有了url长度限制,下面介绍几个解决方案,每个方案各有优缺点,需要根据应用场景选择不同的解决方案。

Cross-subdomain solution###

原理图:


页面A和页面B拥有同一个父域,可以通过设置document的domain属性来通信,栗子:A的域:www.xyy.com,B的域:chifan.xyy.com,这2个源有共同的父域xyy.com,就可以同时在A和B的html页面中这样设置:document.domain=xyy.com, 只能设置成父域,否则会报错:Failed to set the 'domain' property on 'Document': 'corp.elong' is not a suffix of ‘127.0.0.1’。同一个父域下的2个子域通过设置共同的domain属性可以互相通信,这种解决方案适用内网应用。

那么现在问题来了,如果2个页面没有同一个父域怎么办?就用下面一个稍微迂回的方法。

Cross-fragment technique###

原理图:


在这个图中如果A想和iframeB交互,A首先会创建一个iframe,这个frame指向和B有着共同域名的“proxy C”,在C的url中
包含要发给B的所有参数、数据、frame标识。上代码:
function sendMsg(msg){
var frame = document.createElement(“iframe”);
var baseProxy = “http://www.otherapp.com/proxy.html”;
var request = {frameName:’otherApp’,data:msg};设置要交互的frame名称
frame.src = baseProxy+”#”+encodeURI (dojo.toJson(request));//把要发送的msg添加到proxy的url里
frame.style.display=”none”;
document.body.appendChild(frame);
}
当C加载后,会从url获取A发来的数据,并且调用B的一个方法,因为B和C是同一个域名,所以C可以直接调用B的方法。
同理,B可以用统样的方法给A返回数据。
window.onLoad = function(){
var hash = window.location.hash;
if(hash && hash.length>1){
var request = hash.substring(1,hash.length);
var obj = dojo.fromJson(decodeURI (request));//从url的hash部分取出数据
var data = obj.data;
//process data
parent.frames[obj.frameName].getData(…);// 调用frameB的getData方法
}
}

URL.hash(fragment id) solution###

一个url由几部分组成,见图:



一般的,改变一个url会导致页面的刷新,改变hash部分除外。(hash:我们俗称的锚点,#后面跟着一个字符串,不会被当做参数解析) 改变url的hash不会导致刷新页面,hash目前被广泛应用到
web 2.0当部分刷新页面时标志每一步操作。在跨域交互时hash是一个很有用的特性,不同源之间的文档可以设置其他源
url的hash,尽管它们在获取对方的hash时有限制,不同源之间可以通过hash发送消息。
栗子:
原理图:


image.png

用几段代码说明一下:
从A向B发送数据:
function sendMsg(originURL, msg){
var data = {from:originURL, msg:msg};
var src = originURL + “#” + dojo.toJson(data); //把要发送的消息放到hash
document.getElementById('domainB').src=src;
}

B监听从A过来的消息:
window.oldHash="";
checkMessage = function(){
var newHash = window.location.hash; //获取当前页面url的hash
if(newHash.length > 1){
newHash = newHash.substring(1,newHash.length);
if(newHash != oldHash){ //如果检测到和过去的hash不同,就向A发送消息
oldHash = newHash;
var msgs = dojo.fromJson(newHash);
var origin = msgs.from;
var msg = msgs.msg;
sendMessage(origin, "Hello document A");
}
}
}
window.setInterval(checkMessage, 1000); //每隔1秒检查一次hash
sendMessage = function(target, msg){
var hash = "msg="+ msg;
parent.location.href= target + “#” + hash;
}

就像jsonp一样,这个方法也有长度限制,但是它可以更好的进行错误处理。如果要传递一些像?的保留字符,
需要encode一下。
function sendMsg(originURL, msg){

var src = originURL + “#” + encodeURI (dojo.toJson(data));

}
jsonp为啥有长度限制?通过把不同源的url放在<script src=“other.origin.com:1001”></script>标签src的属性,如果想要传递参数,需要把所有要传递的参数放在get请求的url里,src有长度限制,因此传递的数据也有长度限制。jsonp参考:https://web.archive.org/web/20160304044218/http://www.json-p.org/

OpenAjax implementation###

openAjax基于fragment id 和 cross-frame 实现跨域交互,简单来说,openAjax统一管理不同源之间的数据交互,负责管理的模块暂且叫成“master”,master有一个message的容器用来存message,每个源的iframe有自己的client side,client side有自己的container,当iframe想要发消息时,是它自己的container代替它向master的container发送消息,其他iframe通过自己的container监听master的消息,工作原理见下图。


Window.name solution###

window的name属性特性:当页面重新加载后name的值不变,window的name属性可以被设置,利用这个特性实现跨域数据交互。原理图:


image.png

当A想要获取B的内容,A创建一个隐形的iframeB,指向B的url,当获取数据之后,在iframeB中把window.name设置成返回的数据,这时候把页面重定向到A的域名,A从window.name即可获得返回的数据。通过window.name传递数据的长度比hash要多的多,大多数现代浏览器支持window.name传输16M+的数据。

H5中关于跨域传输的新特性###

window.postMessage(message, targetOrigin)实现安全跨域交互。当调用这个函数时,会分发一个消息事件,如果window正在监听这个消息事件,它可以获得消息内容并且知道发消息的源哪个,下面是栗子:

image.png

http://www.otherapp.com/index.html
function postMessage(msg){
var targetWindow = parent.window;
targetWindow.postMessage(msg,"*”);//触发消息事件
}
function handleReceive(msg){
var object = dojo.fromJson(msg);
if(object.status == “ok”){
//continue to do other things
……
}else{
//retry sending msg
……
}
}
window.addEventListener("message", handleReceive, false); //注册监听的事件
window.onLoad = function(){
postMessage("already loaded");
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容