当前位置: 首页 > 科技观察

前端跨域整理

时间:2023-03-19 22:47:17 科技观察

前言原文地址:前端跨域总结博主博客地址:Damonare的个人博客相信每个前端er都对跨域这个词不陌生,它被广泛使用在实际项目中。但是跨域方法五花八门,真是让人眼花缭乱。按照惯例,遇到这种情况只能自己总结一篇博客作为记录。正文1.什么是跨域?跨域这个词的字面意思是跨域名,但实际上跨域的范围肯定不止那么窄。具体概念如下:只要协议、域名、端口不同,就认为是不同的域。跨域问题的原因其实很好理解。如果随便引用外部文件,不同标签下的页面引用相似的文件,浏览器很容易混淆,安全性也得不到保障。立刻。一切都是安全的***。但是在安全限制的同时注入iframe或者ajax应用也带来了很多麻烦。所以我们需要通过一些方法让本域的js可以操作其他域的页面对象或者让其他域的js可以操作本域的页面对象(iframe之间)。下面详细解释具体的跨域情况:URL表示是否允许通信http://www.a.com/a.jshttp://www.a.com/b.jshttp://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同一个域名,不同的二级域名(同上)不允许(这种情况不允许cookies访问)http://www.cnblogs.com/a.jshttp://www.a.com/b.js不允许使用不同的域名这里需要注意两点:如果是协议和端口导致的跨域问题,“前台”无能为力;在跨域问题上,域只是通过“URL的header”来识别,不会去判断同一个ip地址对应的是两个域还是两个域在同一个ip上。(“URL的头部”指的是window.location.protocol+window.location.host,也可以理解为“域、协议、端口必须匹配”。)2.通过document.domain跨域如前所述早前,浏览器有同源策略的局限之一是无法通过ajax请求来自不同来源的文档。第二个限制是浏览器中不同域的框架之间不能进行js交互。不同框架之间可以获取到window对象,但是无法获取到相应的属性和方法。比如有一个页面,它的地址是http://www.damonare.cn/a.html,这个页面里面有一个iframe,它的src是http://damonare.cn/b.html,显然,这个页面和里面的iframe在不同的域,所以我们无法通过在页面中写js代码获取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:在页面中也设置document.domainhttp://damonare.cn/b.html:修改document.domain的方法只适用于框架间的交互不同的子域。3、通过location.hash跨域,因为父窗口可以读写iframe的url,iframe也可以读写父窗口的url。URL的一部分称为哈希,它是#符号及其后面的字符。一般用于浏览器锚点定位,服务器端不关心这部分,应该说在HTTP请求过程中不会携带hash,所以这部分修改不会产生HTTP请求,但会生成浏览器历史记录。该方法的原理是改变URL的hash部分,进行双向通信。每个窗口通过改变其他窗口的location来发送消息(因为两个页面不在同一个域,IE和Chrome不允许修改parent.location.hash的值,所以代理域名下的iframe使用父窗口),并通过监听自己的URL中的变化来接收消息。这种通信方式会造成一些不必要的浏览器历史记录,而且有些浏览器不支持onhashchange事件,需要轮询才能知道url的变化,数据容量和类型受限等。下面举例说明:如果父页面是baidu.com/a.html,iframe嵌入的页面是google.com/b.html(域名和其他这里省略url属性),两个页面之间的通信可以通过下面的方法实现。a.html向b.html传数据修改a.html下iframe的src为google.com/b.html#pacob.html监听到url变化,触发相应操作b.html向a传数据。html,因为这两个页面不在同一个域中。IE和Chrome不允许修改parent.location.hash的值,所以在父窗口域名下的代理iframeb.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的urlhash)a.html检测到url变化,触发相应的操作。b.html页面的关键代码如下:try{parent.location.hash='data';}catch(e){//即chrome的安全机制不能修改parent.location.hash,varifrproxy=document.createElement('iframe');ifrproxy.style.display='none';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.hashparent.parent的值。location.hash=self.location.hash.substring(1);4.跨域高级浏览器InternetExplorer8+通过HTML5的postMessage方法、chrome、Firefox、Opera和Safari都将支持此功能。该功能主要包括接收消息的“message”事件和发送消息的“postMessage”方法。例如damonare.cn域的A页面通过iframe嵌入了google.com域的B页面,A和B之间的通信可以通过以下方式实现:A页面通过postMessage方法发送消息:window.onload=function(){varifr=document.getElementById('ifr');vartargetOrigin="http://www.google.com";ifr.contentWindow.postMessage('helloworld!',targetOrigin);};postMessage的使用方法:otherWindow.postMessage(message,targetOrigin);otherWindow:指的是目标窗口,即向哪个窗口发送消息,是window.frames属性的成员或者window.open方法创建的窗口message:是要发送的消息,类型为String、Object(IE8、9不支持)targetOrigin:限制接收消息的范围。如果没有限制,请使用'*B页面通过消息事件监听接收消息:varonmessage=function(event){vardata=event.data;//messagevarorigin=event.origin;//消息源地址varsource=event.source;//源窗口对象'){window.addEventListener('message',onmessage,false);}elseif(typeofwindow.attachEvent!='undefined'){//foriewindow.attachEvent('onmessage',onmessage);}出于同样的原因,或者B页面可以发送消息,然后A页面监听并接受消息。5、通过jsonp跨域刚才说的类型都是双向通信,即两个iframe,一个页面与一个iframe之间或者一个页面与一个页面之间,下面是几种单项跨域(一般用于获取数据),因为通过script标签引入的js不受同源策略限制。所以我们可以通过script标签引入一个js或者一个其他后缀的文件(比如php、jsp等),这个文件返回一个js函数调用。比如有一个页面a.html,里面的代码需要使用ajax获取异域的json数据。假设json数据地址为http://damonare.cn/data.php,那么a.html中的代码可以这样写:我们看到在获取数据的地址后面有一个回调参数。按照惯例,使用这个参数名,但是你使用其他的也是一样的。当然,如果获取数据的jsonp地址页面不在你的控制范围内,就得按照数据提供方规定的格式进行操作。因为是作为js文件导入的,http://damonare.cn/data.php必须返回一个可执行的js文件,所以这个页面的php代码可能是这样的(必须和后端约定好):最后输出结果为:dosomething(['a','b','c']);如果你的页面使用了jquery,那么通过它封装的方法进行jsonp操作是非常方便的。jquery会自动生成一个全局函数来替换callback=?中的问号,然后在拿到数据后自动销毁,实际上起到了一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域。如果不是跨域,则调用普通的ajax方法;如果是跨域,会以异步加载js文件的形式调用jsonp回调函数。JSONP的优缺点JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受同源策略的限制;它具有更好的兼容性,可以在旧浏览器中运行而无需XMLHttpRequest或ActiveX支持;请求完成后调用callback可以返回结果。JSONP的缺点是:只支持GET请求,不支持POST等其他类型的HTTP请求;只支持跨域的HTTP请求,无法解决不同域的两个页面之间如何进行JavaScript调用的问题。6、通过CORS跨域CORS(Cross-OriginResourceSharing)跨域资源共享定义了浏览器和服务器在访问跨域资源时应该如何通信。CORS背后的基本思想是使用自定义HTTP标头允许浏览器与服务器通信以确定请求或响应应该成功还是失败。目前所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程由浏览器自动完成,无需用户参与。对于开发者来说,CORS通信和同源AJAX通信没有区别,代码完全一样。浏览器一旦发现AJAX请求是跨域的,就会自动添加一些额外的header信息,有时会有额外的请求,但是用户是感觉不到的。因此,实现CORS通信的关键是服务器。只要服务端实现了CORS接口,就可以实现跨域通信。通常的ajax请求可能是这样的:上面的damonare部分是一个相对路径。如果我们要使用CORS,相关的Ajax代码可能是这样的:/trigkit4/",true);xhr.send();这段代码和前面代码的不同之处在于,将相对路径换成了另外一个域的绝对路径,也就是你要访问的接口跨域地址。服务器对CORS的支持主要是通过设置Access-Control-Allow-Origin来实现的。如果浏览器检测到相应的设置,就可以允许Ajax跨域访问。关于CORS的更多信息可以阅读阮一峰老师的这篇文章:跨域资源共享CORS详解CORS与JSONP对比JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获取数据,比JSONP有更好的错误处理。JSONP主要由旧浏览器支持,它们通常不支持CORS,而大多数现代浏览器已经支持CORS)。与JSONP相比,CORS无疑更先进、更方便、更可靠。7、通过window.name,跨域window对象有一个name属性,它有一个特点:即在一个窗口(window)的生命周期中,该window加载的所有页面共享同一个window.name。每个页面都有对window.name的读写权限,window.name持久化在一个窗口加载的所有页面中,不会在加载新页面时重新设置。例如:我们在任意页面输入window.name="Mywindow'sname";setTimeout(function(){window.location.href="http://damonare.cn/";},1000)进入damonare.cnpage然后我们会检测然后检测window.name:window.name;//我的window'sname可以看到如果我们跳转到某个标签中的网页,我们的window.name是不会改变的。基于这个思路,我们可以在某个页面设置window.name的值,然后跳转到另一个页面。在这个页面中,我们可以得到刚才设置的window.name。出于安全原因,浏览器将始终将window.name保留为字符串。同样的方法也可以应用于与iframe的交互:例如:我的页面(http://damonare.cn/index.html)嵌入了一个iframe:在iframe.html中,window.name设置为我们要传递的字符串。我们在index.html中编写了以下代码:variframe=document.getElementById('iframe');vardata='';iframe.onload=function(){data=iframe.contentWindow.name;};Boom!错误!可以肯定的是,因为两个页面的来源不同,如果要解决这个问题,可以这样做:onload=function(){data=iframe.contentWindow.name;}iframe.src='about:blank';};或者将里面的about:blank替换成同源页面(about:blank,javascript:和data:content,继承加载它们的页面的来源。)与document.domain方法相比,这种方法放宽了对域名后缀必须相同,并且可以从任何页面获取字符串类型的数据。后记其他如跨域中间件、serverproxy跨域、FlashURLLoader跨域、动态创建的script标签(jsonp的简化版)等不再赘述。