跨域

1.什么是同源策略

  • 浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。

  • 同源指的是:

    • 同协议
    • 同域名
    • 同端口
  • 作用:保证用户信息的安全,防止恶意的网站窃取数据

    • 例1:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。除此之外,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。(因为浏览器同时还规定,提交表单不受同源政策的限制)
    • 例2:恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码,从而造成相关的风险。
  • 对于当前页面来说页面中 JS 文件的域不重要,重要的是当前页面所在的域与 脚本中涉及到的域(例如xht的open方法的url)是否同源

2.什么是跨域?跨域有几种实现形式

  • 跨域:浏览器出于安全方面的考虑设置了同源策略来限制不同源之间的交互,但是也阻碍了不同域之间的协助。为了实现不同源之间的交互、协作,因此需要“跨域”。

  • 跨域的几种实现形式:

    • JSONP(填充式JSON或参数式JSON)
    • CORS(跨源资源共享)
    • 降域
    • postMessage

3.JSONP的原理是什么?

  • 基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。传回后回调函数立即执行(参数是后端产生的数据),从而实现相应的功能。
  • 具体流程:
    1.网页动态地插入<script>元素,由它向跨域网址发出请求
function addScriptTag(src){
    var script = document.createElement('script');
    script.src = src; //跨域网址
    document.body.appendChild(script);  
    // 往页面插入元素后,会向跨域网址发出请求(src指定了跨域网址,得到响应后立即执行
}

window.onload = function(){
    addScriptTag("http://example.com/ip?callback = foo');  //当页面加载完毕,即往页面中插入script元素
}

function foo(data){
    console.log('your public ip address is: '+ data.ip)'
}

2.上面代码通过动态添加<script>元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。
3.服务器收到该请求后,会将数据放在回到函数的参数位置返回

foo({
  "ip": "8.8.8.8"
});

4.由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

  • 注意:JSONP只能发 GET 请求(因为请求是放在<script>的scr中的)。

4.CORS是什么?

  • CORS: Cross-Origin Resource Sharing, 跨源资源共享,是W3C的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通,它是一种 ajax跨域请求资源的方式,支持现代浏览器,IE支持10以上。

  • 实现过程:当使用 XMLHttpRequest发送请求时,浏览器发现该请求不符合同源策略,会自动给该请求加一个请求头:Origin,并将请求发送。服务器端收到请求后,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器收到响应后判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。

  • 整个CORS通信过程,都是浏览器自动完成,不需要用户参与

  • 实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

  • 详细流程:
    1.浏览器发现这次请求不符合同源策略,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

在上面的头信息中,Origin字段表明了本次请求来自哪个源:协议、域名、端口。

2.该请求到达服务器后,服务器会根据这个值来判断是否接受请求。如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段(如下所示)。如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

3.浏览器收到响应后判断该相应头中是否包含Origin的值,如果响应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获,这时我们无法拿到响应数据。如果有则浏览器会处理响应,我们就可以拿到响应数据。

5.降域

  • 降域获取同一Cookie:Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。
// A网页和B网页设置相同的document.domain
document.domain = 'example.com'
// A网页通过脚本设置Cookie
document.cookie = "test1 = hello";
// B网页可以获取到该cookie
var otherCookie = document.cookie;
  • 降域使不同源的iframe窗口和父窗口相互通信:如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和与父窗口无法通信。如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain属性,就可以规避同源政策,拿到DOM。
A 网页
URL: http://a.yanxin.com:8080/a.html
<div class="ct">
  <h1>使用降域实现跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.yanxin.cn:8080/a.html">
  </div>
  <iframe src="http://b.yanxin.com:8080/b.html" frameborder="0" ></iframe>
</div>

<script>

document.querySelector('.main input').addEventListener('input', function(){
  console.log(this.value);
  /* window.frames 是窗口中所有命名的框架组成的数组。
  这个数组的每个元素都是一个Window对象,对应于窗口中的一个框架。
  window.frames[0]得到的就是html中的框架
  window.frames[0].document.querySelector('input') 得到框架中的input元素
  */
  window.frames[0].document.querySelector('input').value = this.value;
})

document.domain = "yanxin.com"
</script>
iframe中的B网页
URL: http://b.yanxin.com:8080/b.html
<input id="input" type="text"  placeholder="http://b.yanxin.com:8080/b.html">
<script>

document.querySelector('#input').addEventListener('input', function(){
    // 得到父窗口中的input元素
    window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'yanxin.com';
</script>

6.postMessage

  • HTML5为了解决跨域问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。
    这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。

  • 目的:向另一个地方传递数据,另一个地方指的是:包含在当前页面的<iframe>元素,或者由当前页面弹出的窗口

  • window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent 消息。 * otherWindow.postMessage(message, targetOrigin, [transfer]);

    • otherWindow:其他窗口的一个引用(相对于当前的窗口的其他窗口),比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
    • message将要发送到其他 window的数据
    • targetOrigin:指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
    • 注意:如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
  • 示例

页面A:  http://a.yanxin.cn:8080/a.html
在页面A中打开页面B: http://b.yanxin.cn:8080/b.html
当点击页面A上的button时,向页面B传输消息"hello world"
  <button id="sendmessage">send message</button>
  <script>
   var button = document.querySelector("#sendmessage");
  // 打开页面b,并且取得对它的引用
   var targetWindow = window.open("http://b.yanxin.cn:8080/b.html");
   button.addEventListener('click',function(){
   // 注意: 这里是向targetWindow即新打开的窗口(通过上面的window.open打开的窗口)发送消息
  // 直接打开页面b(手动打开的)是无法收到消息的
  // 如果直接调用postMessage则相当于当前窗口向自己发送消息
      targetWindow.postMessage("hello world!!", "http://b.yanxin.cn:8080/b.html");
   });
   
  </script>
为页面B的window添加监听器
当页面B收到Message时,在控制台中输出收到的消息,并提示"hello"
<script>
    window.addEventListener("message", receiveMessage);
    function receiveMessage(event){
        console.log(event.data);
        alert("hello");
    }
</script>

参考资料:

CORS 详解-阮一峰
浏览器同源政策及其规避方法-阮一峰
Window.postMessage() - Web APIs | MDN

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

推荐阅读更多精彩内容

  • 跨域问题的场景和解决方案多种多样,只要是做前端开发,总会遇到。而且面试时也是必问的问题。所以自己学习总结记录一下。...
    花开_陈凤娟阅读 730评论 0 0
  • 欢迎关注微信公众号:全栈工厂 本文主要参考跨域资源共享 CORS 详解[http://www.ruanyifeng...
    liqingbiubiu阅读 1,798评论 0 3
  • 一、浏览器的同源策略 1.什么是同源? 所谓“同源”指的是”三个相同“。相同的域名、端口和协议,这三个相同的话就视...
    徐国军_plus阅读 825评论 1 3
  • 什么是同源策略 浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能...
    ezrealor阅读 481评论 0 1
  • 因为初三没有考上高中,只能听父母的话来到了现在就读的学校,来到这个学校最幸福的事就是遇到他,最初对他的了解还是...
    Bunuan阅读 149评论 0 0