常见的跨域解决方案

1、JSONP

受浏览器同源策略的限制,网页无法向其他域发送ajax请求,但在页面中引入其他域的脚本是可以的,最常见的例子就是在页面中引入cdn服务器上的js文件及图片等静态资源,JSONP正是利用这一特性实现了跨域。
例如,http://test.com/test.html获取http://abc.test.com/data.php上的数据,那么只需要在test.html中加入以下代码即可:

<script>
    function handleData(data){
        // do something
    }
</script>
<script scr="http://abc.test.com/data.php?callback=handleData"></script>

其实第二个标签返回的是一个可执行的js文件,data.php包含这样一段代码:

<?php
$callback = $_GET['callback']; 
$data = array(1,2);
echo $callback.'('.json_encode($data)')'
?>

当第二个标签加载完毕后,就会执行handleData([1,2])

2、document.domain

浏览器同源策略中第二个限制是不同域的框架之间不能进行js交互(但可获得彼此的window对象)
例如,在http://abc.test.com/test.html这个页面中有一个iframe起src为http://bcd.test.com/test2.html,以下代码会出现注释中的问题

//test.html
<script>
    function ifrLoad(){
        var iframe=document.getElementById('iframe');
        var ifrWin=iframe.contentWindow; //获得iframe的window对象(但属性和方法几乎不可用)
        console.log(ifrWin.document); //无法获得document属性
    }
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" ></iframe>

此时可通过document.domain设置当前文档的原始域实现跨域操作,只要两个页面的原始域相同,就可通过js获得iframe中的各种属性和方法了.

<!-- test.html   -->
<script>
   document.domain='test.com'
   function ifrLoad(){
       var iframe=document.getElementById('iframe');
       var ifrWin=iframe.contentWindow; //获得iframe的window对象(但属性和方法几乎不可用)
       console.log(ifrWin.document);  //正常
   }
</script>
<iframe src="https:www.baidu.com" id="iframe" onload="ifrLoad()" ></iframe>
<!-- test2.html -->
<script>
    document.domain='test.com'
</script>

需要要注意的是,d只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.test.com 中某个文档的document.domain 可以设成a.b.test.com、b.test.com 、test.com中的任意一个,但是不可以设成 c.a.b.test.com,因为这是当前域的子域,也不可以设成example.com,因为主域已经不相同了。

3 、window.name

window.name属性在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)假设我们在百度首页设置了window.name为'baidu',

我们在同一窗口地址栏中输入谷歌网址,此时我们查看window.name会发现window.name依然为‘baidu’

此时我们修改window.name为'google',再次打开百度,发现window.name变为了‘google’


了解了window.name属性之后,我们想要从test页面中获得test1中的数据就容易多了,可以首先加载test1页面,将test页面需要的数据存在window.name中,然后加载test页面,但貌似这种方式很蠢,解决办法是在test页面中设置一个隐藏的iframe标签用于获取test2页面的window.name,但需要注意的是受同源策略的限制,需要在iframe中的test2加载完毕后,在iframe中加载与test同源的test3页面。

<!-- http://abc.test.com/test.html   -->
<script>
    function loadData(){
        var iframe=document.getElementById('iframe');
        iframe.onload=fucntion(){
            var data=iframe.contentWindow.name
            console.log(data)
        }
        iframe.src='http://abc.test.com/test3.html'

    }
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="loadData()" ></iframe>

4、window.postMessage

window.postMessage是HTML5的API,允许跨域在两个窗口/frames之间发送数据。需要注意的是调用postMessage方法的window对象是指接受消息的那个window对象。其调用方式如下:

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow 是其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
argetOrigin 是用来接收消息的那个window对象所在的域,如果不希望做限定,可以用*代替。
transfer 可选,是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

<!-- http://abc.test.com/test.html   -->
<script>
    function ifrLoad(){
        var iframe=document.geElementById('iframe');
        iframe.contentWindow.postMessage('hello world!','*')
    }
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" '></iframe>
<!-- test2.html -->
<script>
    window.onmessage=function(e){
        console.log(e.data)
    }
</script>

5 、跨域资源共享(CORS)

服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求,但需要浏览器需要支持HTML5,可以支持POST,PUT等方法

HTML5标准中提出的跨域资源共享(Cross Origin Resource Share,CORS)支持其他的HTTP方法如PUT, POST等,可以从本质上解决跨域问题。
例如,从http://a.com要访问http://b.com的数据,通常情况下Chrome会因跨域请求而报错:

XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.

错误原因是被请求资源没有设置Access-Control-Allow-Origin,所以我们在b.com的服务器中设置这个响应头字段即可:

Access-Control-Allow-Origin: * # 允许所有域名访问,或者
Access-Control-Allow-Origin: http://a.com # 只允许所有域名访问

为 xhr设置 withCredentials后CORS方法跨域还可携带Cookie,但 PUT/POST 请求需要注意处理 preflight 请求

6、Proxy

可以在服务器端设置一个代理,由服务器端向跨域下的网站发出请求,再将请求结果返回给前端,成功避免同源策略的限制。

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

推荐阅读更多精彩内容

  • 什么是同源策略 同源策略是指浏览器处于安全方面的考虑只允许本域下的接口交互。不同源的客户端脚本在没有明确授权的情况...
    夜流光丶阅读 267评论 0 0
  • 什么是跨域? 2.) 资源嵌入:、、、等dom标签,还有样式中background:url()、@font-fac...
    电影里的梦i阅读 2,361评论 0 5
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    w_zhuan阅读 510评论 0 0
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    他在发呆阅读 822评论 0 0
  • 打嗝其实是人类进化的产物,也是身体的保护反应。 在胸腔和腹腔之间,有一个肌肉膜叫膈肌,厚厚的、像帽子,把胸腔和腹腔...
    美妙的养生阅读 324评论 0 0