说说跨域那些事儿

首先纠正一个误区,跨域并非浏览器限制了发起跨站请求的这种能力,恰恰相反,我们可以发出请求,服务端也可以接收到请求并正常返回数据,只不过在返回之后浏览器会阻止非同源数据(response),从而在控制台打出一系列报错信息。

原文地址:说说跨域那些事儿,迁移到简书上来和大家共享

兼容性查找

文章中会涉及一系列兼容性的图解(mdn & can i use)和一些专有名词(mdn),可以通过两个渠道来查看

跨域类型

定义就不说了,从字面也可以看其含义,首先我们认识下哪些情况属于跨域,可以分为以下几点:

  • 协议不同,如http, https;
  • 端口不同;
  • 主域相同,子域不同;
  • 主域不同;
  • ip地址和域名之间也算是跨域,浏览器不会自动做ip域名的映射;

解决方案

  • document.domain
  • window.name
  • jsonp
  • postMessage
  • cors

document.domain

  • 关键点
    • 跨域分为两种,一种xhr不能访问不同源的文档,另一种是不同window之间不能进行交互操作;
    • document.domain主要是解决第二种情况,且只能适用于主域相同子域不同的情况;
    • document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com中某个文档的document.domain可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。
  • 兼容性:所有浏览器都支持;
  • 优点:
    • 可以实现不同window之间的相互访问和操作;
  • 缺点:
    • 只适用于父子window之间的通信,不能用于xhr;
    • 只能在主域相同且子域不同的情况下使用;
  • 使用方式
    • a(当前页面或父页面)页面中加入document.domain = 'example.com';
    • b(当前页面或子页面)页面中加入document.domain = 'example.com';
    • a页面访问b页面里面的数据或者方法;

window.name

  • 关键点:window.name在页面的生命周期里共享一个window.name;
  • 兼容性:所有浏览器都支持;
  • 优点:
    • 最简单的利用了浏览器的特性来做到不同域之间的数据传递;
    • 不需要前端和后端的特殊配制;
  • 缺点:
    • 大小限制:window.name最大size是2M左右,不同浏览器中会有不同约定;
    • 安全性:当前页面所有window都可以修改,很不安全;
    • 数据类型:传递数据只能限于字符串,如果是对象或者其他会自动被转化为字符串,如下;


  • 使用方式:修改window.name的值即可;

jsonp

  • 关键点:浏览器对XHR做了同源策略,但并没有将这种方式延续到script上(其实还有iframe,img等),从而可以利用动态script标签技术来做到跨域请求的作用。至于为什么会这样设计,本人也不太清楚,有可能是历史遗迹(漏洞),有可能是某些方面的技术瓶颈,也有可能是为了满足某些需求专门定制的,总之这项技术方案我们过去可以用,现在可以用就ok,至于将来应该也是会存在的,毕竟现在已经应用在很多家站点上,就算会废弃,也会有一段时间迭代。
  • 兼容性:所有浏览器都兼容这种方式;
  • 优点:很明显前端可以很轻松的做到跨域请求;
  • 缺点
    • 只能通过GET方式请求,一方面是参数长度有限制,二是安全性比较差;
    • 后端需要知道前端的cb是什么样的结构,主要在参数和回调名;
    • 后端需要进行参数和cb的拼接然后才能执行;
  • 使用方式
/** 前端生成script标签,并将src中传入需要执行的callback **/
<script type="text/javascript">
    var ele = document.createElement('script');
    ele.type = "text/javascript"
    ele.src = "http://example.com?jsonp=cb";
    document.body.appendChild(ele);
</script>


/** 后端接到参数后给callback加入参数并执行 **/
<?php
    $cb = $_GET['cb'];
    $data = array('test1', 'test2', 'test3');
    echo $cb.'('.json_encode($data).')';
?>

postMessage

  • 关键点:postMessage是h5引入的一个新概念,现在也在进一步的推广和发展中,他进行了一系列的封装,我们可以通过window.postMessage的方式进行使用,并可以监听其发送的消息;
  • 兼容性:下图是postMessage的兼容图,移动端可以放心用,但是pc端需要做降级处理,具体可以根据文中介绍的这几种跨域方式来则情选择;


    poseMessage兼容性
  • 优点
    • 不需要后端介入就可以非常简单的的做到跨域,一个函数外加两个参数(请求url,发送数据)就可以搞定;
    • 移动端兼容性好;
  • 缺点
    • 无法做到一对一的传递方式:监听中需要做很多消息的识别,由于postMessage发出的消息对于同一个页面的不同功能相当于一个广播的过程,该页面的所有onmessage都会收到,所以需要做消息的判断;
    • 安全性问题:三方可以通过截获,注入html或者脚本的形式监听到消息,从而能够做到篡改的效果,所以在postMessage和onmessage中一定要做好这方面的限制;
    • 发送的数据会通过结构化克隆算法进行序列化,所以只有满足该算法要求的参数才能够被解析,否则会报错,如function就不能当作参数进行传递;
  • 使用方式:下面是前段时间写的一个通信的函数,sendMessage_负责发送消息,bindEvent_负责消息的监听并处理,可以通过代码来做一个大致了解;
Storage.prototype.sendMessage_ = function(type, params, fn) {
    if (this.topWindow) {
        this.handleCookie_(type, params, fn);
        return;
    }
    var eventId = this.addToQueue_(fn, type);
    var storageIframe = document.getElementById('mip-storage-iframe');
    var element = document.createElement("a");
    element.href = this.origin;
    var origin = element.href.slice(0, element.href.indexOf(element.pathname) + 1);     

    storageIframe.contentWindow.postMessage({
        type: type,
        params: params,
        eventId: eventId
    }, origin);
}

Storage.prototype.bindEvent_ = function() {
    window.onmessage = function (res) {
        // 判断消息来源           
        if (window == res.source.window.parent &&
            res.data.type === this.messageType.RES &&
            window.location.href.match(res.origin.host).length > 0) {               
            var fn = this.eventQueue[res.data.eventId];
            fn && fn();
            delete this.eventQueue[res.data.eventId];
            // reset id
            var isEmpty = true;
            for (var t in this.eventQueue) {
                isEmpty = false;
            }
            if (isEmpty) {                  
                this.id = 0;
            }
        }                   
    }.bind(this);
}

cors

  • 关键点:cors是一种通过前后端http header配置来进行跨域的一种方式;
  • 兼容性:如果不考虑pc端的IE,移动端的opera的话那兼容性还是不错的,针对ie和opera可以做适当的降级处理;


    poseMessage兼容性
  • 安全策略
    • 请求
      • origin:通过http头中的origin判断域名是否是允许的;
      • Example-Same-origin:如果http origin不存在,最好能够自己在请求头中加入该参数来标示是否是同源,true表示请求来自于同域名下(同域名下请求不带origin);如果该字段存在并且为true则允许请求接口,否则禁止;
      • Example_source_origin:该参数同origin,是在origin不存在的情况下用来标示请求来源的url
    • 返回
      • Access-Control-Allow-Origin: originorigin表示允许哪些网站请求,不建议设置为*;
      • Access-Control-Expose-Headers:Example-Access-Control-Allow-Source-Origin,允许http返回中包含该字段,可以通过这种方式在返回头中加入自定义字段,如该例子中的Example-Access-Control-Allow-Source-Origin;
  • 优点
    • 前端方便不少,只需要发请求而不用考虑跨域问题;
    • 安全性能够得以控制和保障;
  • 缺点
    • 兼容性不全面,需要做降级处理;
  • 使用方式
    • 正常请求即可,无论是你要用xhr,还是用一些封装好的组件,如fetchfetchJsonp,亦或是jquery一类的技术均可;
    • 后端在response时需要设置一定的配置参数,并保证安全策略,具体方案可以参照下面安全策略模块;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容

  • 跨域资源共享 CORS 对于web开发来讲,由于浏览器的同源策略,我们需要经常使用一些hack的方法去跨域获取资源...
    默默先生Alec阅读 580评论 0 0
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    w_zhuan阅读 505评论 0 0
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    他在发呆阅读 821评论 0 0
  • 我有一个亲姐姐,大我两岁。 我们姐妹俩长得并不太像,小时候为一点食物或生活用品就可以打架,而谁能想到,她现在竟然已...
    cora的生活手册阅读 201评论 0 0
  • 昨天品思微分享,傅徹师父分享了“一句话的力量”。让我反思了和女儿之间的细节。 3月10日 下班回家,刚下车,女儿就...
    乖兔妈阅读 338评论 0 2