首页 web前端 js教程 关于前端跨域总结的相关知识点

关于前端跨域总结的相关知识点

May 21, 2018 pm 04:41 PM

本篇对跨域进行了相关的讲解。

前言

关于前端跨域的解决方法的多种多样实在让人目不暇接。以前碰到一个公司面试的场景是这样的,好几个人一起在等待面试,一个个进去面,面试官问:“给我说说跨域的解决方式吧”,吧啦吧啦就说出了(自己在当时情况下脑子里能记住的)三种,然后面试官就说:“你们每个人进来都说了这三种,除了这些,还有哪些?”,顿时凌乱在风中...碰到这种情况,只能自己总结一篇博客,以备查漏补缺。

1. 什么是跨域?

跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议、域名、端口有任何一个不同,都被当作是不同的域。之所以会产生跨域这个问题呢,其实也很容易想明白,要是随便引用外部文件,不同标签下的页面引用类似的彼此的文件,浏览器很容易懵逼的,安全也得不到保障了就。什么事,都是安全第一嘛。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。所以我们要通过一些方法使本域的js能够操作其他域的页面对象或者使其他域的js能操作本域的页面对象(iframe之间)。下面是具体的跨域情况详解:

         URL                                                                         说明                                                                                                                                                        是否允许通信
http://www.a.com/a.jshttp://www.a.com/b.js            同一域名下            允许http://www.a.com/lab/a.jshttp://www.a.com/script/b.js  同一域名下不同文件夹      允许http://www.a.com:8000/a.jshttp://www.a.com/b.js         同一域名,不同端口        不允许http://www.a.com/a.jshttps://www.a.com/b.js        同一域名,不同协议        不允许http://www.a.com/a.jshttp://70.32.92.74/b.js       域名和域名对应ip         不允许http://www.a.com/a.jshttp://script.a.com/b.js      主域相同,子域不同        不允许(cookie这种情况下也不允许访问)http://www.a.com/a.jshttp://a.com/b.js             同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)http://www.cnblogs.com/a.jshttp://www.a.com/b.js         不同域名                  不允许

需要注意两点:

如果是协议和端口造成的跨域问题“前台”是无能为力的;

在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
(“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。)

同源策略

同域名(或ip),同端口,同协议视为同一个域;一个域内的脚本仅仅具有本域内的权限,可以理解为本域脚本只能读写本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略。

同源策略是浏览器最基本的安全功能。如果没有同源策略,那么普通用户将没有安全可言。用户的所有私密信息都可以被被任何人获取,例如网站的Cookie、email的邮件内容。还很容易遭受CSRF攻击。

要注意的是,域名和域名对应ip是不同源的;主域名相同,子域名不相同也是不同源的。

跨域解决方式(总结)

1. document.domain跨域

前面说过了,浏览器有一个同源策略,其限制之一是不能通过ajax的方法去请求不同源中的文档。 第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。
不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。
比如,有一个页面,它的地址是http://www.damonare.cn/a.html , 在这个页面里面有一个iframe,它的src是http://damonare.cn/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中这样书写js代码来获取iframe中的东西的:

<script type="text/javascript">
    function test(){        var iframe = document.getElementById(&#39;ifame&#39;);
登录后复制
   var win = document.contentWindow; //可以获取到iframe里的window对象,但该window对象的属性和方法几乎是不可用的
        var doc = win.document; //这里获取不到iframe里的document对象
        var name = win.name; //这里同样获取不到window对象的name属性
    }</script><iframe id = "iframe" src="http://damonare.cn/b.html" onload = "test()"></iframe>
登录后复制

这个时候,document.domain就可以派上用场了,我们只要把http://www.damonare.cn/a.html和http://damonare.cn/b.html这两个页面的document.domain都设成相同的域名就可以了。

注意: document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。

在页面http://www.damonare.cn/a.html 中设置document.domain:

<iframe id = "iframe" src="http://damonare.cn/b.html" onload = "test()"></iframe><script type="text/javascript">
    document.domain = &#39;damonare.cn&#39;;//设置成主域
    function test(){        //contentWindow 可取得子窗口的 window 对象
        alert(document.getElementById(&#39;iframe&#39;).contentWindow);
    }</script>
登录后复制

在页面http://damonare.cn/b.html 中也设置document.domain:


修改document.domain的方法只适用于不同子域的框架间的交互。

2. 通过location.hash跨域

因为父窗口可以对iframe进行URL读写,iframe也可以读写父窗口的URL,URL有一部分被称为hash,就是#号及其后面的字符,它一般用于浏览器锚点定位,Server端并不关心这部分,应该说HTTP请求过程中不会携带hash,所以这部分的修改不会产生HTTP请求,但是会产生浏览器历史记录。此方法的原理就是改变URL的hash部分来进行双向通信。每个window通过改变其他 window的location来发送消息(由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于父窗口域名下的一个代理iframe),并通过监听自己的URL的变化来接收消息。这个方式的通信会造成一些不必要的浏览器历史记录,而且有些浏览器不支持onhashchange事件,需要轮询来获知URL的改变,最后,这样做也存在缺点,诸如数据直接暴露在了url中,数据容量和类型都有限等。

举例说明:
假如父页面是baidu.com/a.html,iframe嵌入的页面为google.com/b.html(此处省略了域名等url属性),要实现此两个页面间的通信可以通过以下方法:

a.html传送数据到b.html

a.html下修改iframe的src为google.com/b.html#paco

b.html监听到url发生变化,触发相应操作

b.html传送数据到a.html,由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于父窗口域名下的一个代理iframe

b.html下创建一个隐藏的iframe,此iframe的src是baidu.com域下的,并挂上要传送的hash数据,如src=”http://www.baidu.com/proxy.html#data”

proxy.html监听到url发生变化,修改a.html的url(因为a.html和* proxy.html同域,所以proxy.html可修改a.html的url hash)

a.html监听到url发生变化,触发相应操作

b.html页面的关键代码如下:

try {  
    parent.location.hash = &#39;data&#39;;  
} catch (e) {  
    // ie、chrome的安全机制无法修改parent.location.hash,  
    var ifrproxy = document.createElement(&#39;iframe&#39;);  
    ifrproxy.style.display = &#39;none&#39;;  
    ifrproxy.src = "http://www.baidu.com/proxy.html#data";  
    document.body.appendChild(ifrproxy);  
}
登录后复制

proxy.html页面的关键代码如下 :

/**
*因为parent.parent(即baidu.com/a.html)和baidu.com/proxy.html属于同一个域,
所以可以改变其location.hash的值**/ parent.parent.location.hash = self.location.hash.substring(1);

3. 通过HTML5的postMessage方法跨域

高级浏览器IE8+, chrome,Firefox , Opera 和 Safari 都将支持这个功能。
这个功能主要包括接受信息的”message”事件和发送消息的”postMessage”方法。
比如damonare.cn域的A页面通过iframe嵌入了一个google.com域的B页面,可以通过以下方法实现A和B的通信:

A页面通过postMessage方法发送消息:

window.onload = function() {  
    var ifr = document.getElementById(&#39;ifr&#39;);  
    var targetOrigin = "http://www.google.com";  
    ifr.contentWindow.postMessage(&#39;hello world!&#39;, targetOrigin);  
};
登录后复制

postMessage的使用方法:otherWindow.postMessage(message, targetOrigin);

otherWindow:指目标窗口,也就是给哪个window发消息,是window.frames 属性的成员或者由 window.open 方法创建的窗口。

message: 是要发送的消息,类型为 String、Object (IE8、9 不支持)。

targetOrigin: 是限定消息接收范围,不限制请使用 ' * '。

B页面通过message事件监听并接受消息:

var onmessage = function (event) {  
  var data = event.data;//消息  
  var origin = event.origin;//消息来源地址  
  var source = event.source;//源Window对象  
  if(origin=="http://www.baidu.com"){  
console.log(data);//hello world!  
  }  
};  
if (typeof window.addEventListener != &#39;undefined&#39;) {  
  window.addEventListener(&#39;message&#39;, onmessage, false);  
} else if (typeof window.attachEvent != &#39;undefined&#39;) {  
  //for ie  
  window.attachEvent(&#39;onmessage&#39;, onmessage);  
}
登录后复制

同理,也可以B页面发送消息,然后A页面监听并接受消息。

4. jsonp跨域

以上的这几种都是双向通信的,即两个iframe,页面与iframe,或是页面与页面之间的。
下面说几种单向跨域的(一般用来获取数据),因为通过script标签引入的js是不受同源策略的限制的。
所以我们可以通过script标签引入一个js或者是一个其他后缀形式(如php,jsp等)的文件,此文件返回一个js函数的调用。

比如,有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是http://damonare.cn/data.php,那么a.html中的代码就可以这样:


可以看到获取数据的地址后面还有一个callback参数,按惯例是用这个参数名,但是你用其他的也一样。当然如果获取数据的jsonp地址页面不是你自己能控制的,就得按照提供数据的那一方的规定格式来操作了。
因为是当做一个js文件来引入的,所以http://damonare.cn/data.php返回的必须是一个能执行的js文件,所以这个页面的php代码可能是这样的(一定要和后端约定好哦):

$callback = $_GET['callback'];//得到回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出?>

最终,输出结果为:dosomething([‘a’,’b’,’c’]);

如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了:

<script type="text/javascript">
    $.getJSON(&#39;http://example.com/data.php?callback=?,function(jsondata)&#39;){        //处理获得的json数据
    });</script>
登录后复制

jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。
$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

JSONP的优缺点:

JSONP的优点:
它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点:
它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

5. CORS跨域

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。
CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

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

平时的ajax请求可能是这样的:

<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/damonare",true);
    xhr.send();</script>
登录后复制

以上damonare部分是相对路径,如果我们要使用CORS,相关Ajax代码可能如下所示:

<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "http://segmentfault.com/u/andreaxiang/",true);
    xhr.send();</script>
登录后复制

代码与之前的区别就在于相对路径换成了其他域的绝对路径,也就是你要跨域访问的接口地址。

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。
如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
关于CORS更多了解可以看下阮一峰老师的这一篇文章:跨域资源共享 CORS 详解

CORS和JSONP对比

JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。

使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。

CORS与JSONP相比,无疑更为先进、方便和可靠。

6. 通过window.name跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
比如:我们在任意一个页面输入

window.name = "My window&#39;s name";
setTimeout(function(){    window.location.href = "http://damonare.cn/";
},1000)
登录后复制

进入damonare.cn页面后我们再检测再检测 window.name :
window.name; // My window's name

可以看到,如果在一个标签里面跳转网页的话,我们的 window.name 是不会改变的。
基于这个思想,我们可以在某个页面设置好 window.name 的值,然后跳转到另外一个页面。在这个页面中就可以获取到我们刚刚设置的 window.name 了。

由于安全原因,浏览器始终会保持 window.name 是string 类型。

同样这个方法也可以应用到和iframe的交互来,比如:
我的页面(http://damonare.cn/index.html)中内嵌了一个iframe:

在 iframe.html 中设置好了 window.name 为我们要传递的字符串。
我们在 index.html 中写了下面的代码:

var iframe = document.getElementById(&#39;iframe&#39;);var data = &#39;&#39;;
iframe.onload = function() {
    data = iframe.contentWindow.name;
};
登录后复制

报错!肯定的,因为两个页面不同源嘛,想要解决这个问题可以这样干:

var iframe = document.getElementById(&#39;iframe&#39;);var data = &#39;&#39;;
 
iframe.onload = function() {
    iframe.onload = function(){
        data = iframe.contentWindow.name;
    }
    iframe.src = &#39;about:blank&#39;;
};
登录后复制

或者将里面的 about:blank 替换成某个同源页面(about:blank,javascript: 和 data: 中的内容,继承了载入他们的页面的源。)

这种方法与 document.domain 方法相比,放宽了域名后缀要相同的限制,可以从任意页面获取 string 类型的数据。

本篇对跨域做出相应的总结,更多相关知识请关注php中文网。

相关推荐:

前端常见跨域解决方案(全)

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

对于函数事件的总结

以上是关于前端跨域总结的相关知识点的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

PHP Session 跨域问题的解决方法 PHP Session 跨域问题的解决方法 Oct 12, 2023 pm 03:00 PM

PHPSession跨域问题的解决方法在前后端分离的开发中,跨域请求已成为常态。在处理跨域问题时,我们通常会涉及到session的使用和管理。然而,由于浏览器的同源策略限制,跨域情况下默认情况下无法共享session。为了解决这个问题,我们需要采用一些技巧和方法来实现session的跨域共享。一、使用cookie跨域共享session最常

总结Linux系统中system()函数的用法 总结Linux系统中system()函数的用法 Feb 23, 2024 pm 06:45 PM

Linux下system()函数的总结在Linux系统中,system()函数是一个非常常用的函数,它可以用于执行命令行命令。本文将对system()函数进行详细的介绍,并提供一些具体的代码示例。一、system()函数的基本用法system()函数的声明如下:intsystem(constchar*command);其中,command参数是一个字符

PHP与Vue:完美搭档的前端开发利器 PHP与Vue:完美搭档的前端开发利器 Mar 16, 2024 pm 12:09 PM

PHP与Vue:完美搭档的前端开发利器在当今互联网高速发展的时代,前端开发变得愈发重要。随着用户对网站和应用的体验要求越来越高,前端开发人员需要使用更加高效和灵活的工具来创建响应式和交互式的界面。PHP和Vue.js作为前端开发领域的两个重要技术,搭配起来可以称得上是完美的利器。本文将探讨PHP和Vue的结合,以及详细的代码示例,帮助读者更好地理解和应用这两

C#开发经验分享:前端与后端协同开发技巧 C#开发经验分享:前端与后端协同开发技巧 Nov 23, 2023 am 10:13 AM

作为一名C#开发者,我们的开发工作通常包括前端和后端的开发,而随着技术的发展和项目的复杂性提高,前端与后端协同开发也变得越来越重要和复杂。本文将分享一些前端与后端协同开发的技巧,以帮助C#开发者更高效地完成开发工作。确定好接口规范前后端的协同开发离不开API接口的交互。要保证前后端协同开发顺利进行,最重要的是定义好接口规范。接口规范涉及到接口的命

前端面试官常问的问题 前端面试官常问的问题 Mar 19, 2024 pm 02:24 PM

在前端开发面试中,常见问题涵盖广泛,包括HTML/CSS基础、JavaScript基础、框架和库、项目经验、算法和数据结构、性能优化、跨域请求、前端工程化、设计模式以及新技术和趋势。面试官的问题旨在评估候选人的技术技能、项目经验以及对行业趋势的理解。因此,应试者应充分准备这些方面,以展现自己的能力和专业知识。

Django是前端还是后端?一探究竟! Django是前端还是后端?一探究竟! Jan 19, 2024 am 08:37 AM

Django是一个Python编写的web应用框架,它强调快速开发和干净方法。尽管Django是一个web框架,但是要回答Django是前端还是后端这个问题,需要深入理解前后端的概念。前端是指用户直接和交互的界面,后端是指服务器端的程序,他们通过HTTP协议进行数据的交互。在前端和后端分离的情况下,前后端程序可以独立开发,分别实现业务逻辑和交互效果,数据的交

Go语言前端技术探秘:前端开发新视野 Go语言前端技术探秘:前端开发新视野 Mar 28, 2024 pm 01:06 PM

Go语言作为一种快速、高效的编程语言,在后端开发领域广受欢迎。然而,很少有人将Go语言与前端开发联系起来。事实上,使用Go语言进行前端开发不仅可以提高效率,还能为开发者带来全新的视野。本文将探讨使用Go语言进行前端开发的可能性,并提供具体的代码示例,帮助读者更好地了解这一领域。在传统的前端开发中,通常会使用JavaScript、HTML和CSS来构建用户界面

Django:前端和后端开发都能搞定的神奇框架! Django:前端和后端开发都能搞定的神奇框架! Jan 19, 2024 am 08:52 AM

Django:前端和后端开发都能搞定的神奇框架!Django是一个高效、可扩展的Web应用程序框架。它能够支持多种Web开发模式,包括MVC和MTV,可以轻松地开发出高质量的Web应用程序。Django不仅支持后端开发,还能够快速构建出前端的界面,通过模板语言,实现灵活的视图展示。Django把前端开发和后端开发融合成了一种无缝的整合,让开发人员不必专门学习

See all articles