js跨域方法总结(一)

一、什么是跨域
跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址, b页面为域名地址,所进行的访问行动都是跨域。
二、同源策略
浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。
同ip(或domain),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,可以理解为本域脚本只能读写本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略。
现代浏览器在安全性和可用性之间选择了一个平衡点。 在遵循同源策略的基础上,选择性地为同源策略“开放了后门”。例如img script style等标签,都允许垮域引用资源,然而, 你也只能是引用这些资源而已,并不能读取这些资源的内容
同源策略限制以下几种行为:
1.Cookie、userData(IE本地数据存储)、LocalStorage 和 IndexDB 无法读取
2.DOM 和 Js对象无法获得
3.AJAX 请求不能发送(XHR对象遵循同源策略)
三、常见的跨域解决方案
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
10、通过Flash插件发送HTTP请求,这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了。

3.1 jsonp跨域
在js中,XMLHttpRequest遵循同源策略,所以我们不能使用XMLHttpRequest请求不同域上的数据时。但是,我们在页面上引入不同域上的js脚本文件(只能引用,不能读取),jsonp正是利用这个特性来实现的。
<script type="text/javascript">
 var script = document.createElement("script");
 script.src = "http://example.com/data.php?callback=handelResponse";
 document.body.insertBefore(script,document.body.firstChild);
 function handelResponse(data){
  console.log(data);//data是返回的json数据
 }
</script>
jsonp原理 :通过script标签加载异域js文件并执行,通过预先设定好的callback函数来实现和母页的交互。当JS文件成功载入后将执行callback函数,并把数据作为参数传入。
jsonp的缺点 :无法确定响应是否失败;只支持get请求;
jsonp也可以通过jquery实现:$.getJSON(“ http://example.com/data.phpcallback=dosomething ”,function(data){});
原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

3.2 document.domain + iframe跨域
利用document.domain实现不同子域的框架间的交互
浏览器中不同域的框架之间不能进行js的交互操作,虽然能够获取到彼此的window对象的,但却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器比如ie6也可以使用top、parent等少数几个属性),总之,你可以当做是只能获取到一个几乎无用的window对象。例如有一个页面,它的地址是 http://www.example.com/a.html   , 在这个页面里面有一个iframe,它的src是 http://example.com/b.html , 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:
<iframe src="http://example.com/b.html" id="iframe"></iframe>
var iframe = document.getElementById("iframe");
 var win = iframe.contentWindow;//可以获取
 var doc = win.document;//无法获取
 var name = win.name;//无法获取
但我们只要把 http://www.example.com/a.html  和  http://example.com/b.html 这两个页面的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,因为主域已经不相同了。
在页面  http://www.example.com/a.html  中设置document.domain:
document.domain = "example.com";
在页面  http://example.com/b.html  中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:
document.domain = "example.com";
这样我们就可以通过js访问到iframe中的各种属性和对象了。

利用隐藏iframe跨域资源请求
虽然这样设置了设置了相同的document.domain,但仍然无法进行ajax通信,只是实现了不同子域的框架间的交互。如果你想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。原理:让这个iframe中的页面的所在域与你想要通过ajax获取数据的目标页面的域相同,然后利用这个iframe中的页面是与目标页面进行ajax请求,然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发送ajax请求,然后收到的数据我们也可以获得了。

3.3 window.name + iframe跨域
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,其中的任何一个页面都可以对window.name的值进行修改。window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。(对于不同域的页面同样适用)注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。
所以,我们可以利用window.name来进行不同域页面之间的传值。
下面就来看一看具体是怎么样通过window.name来跨域获取数据的。还是举例说明。
比如有一个 www.example.com/a.html 页面,需要通过a.html页面里的js来获取另一个位于不同域上的页面 www.cnblogs.com/data.html 里的数据。
data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要得到的数据值。data.html里的代码:
那么在a.html页面中,我们怎么把data.html页面载入进来呢?显然我们不能直接在a.html页面中通过改变window.location来载入data.html页面,因为我们想要即使a.html页面不跳转也能得到data.html里的数据。答案就是在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。
充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为 www.cnblogs.com/data.html 就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。
看下a.html页面的代码:
上面的代码只是最简单的原理演示代码,你可以对使用js封装上面的过程,比如动态的创建iframe,动态的注册各种事件等等,当然为了安全,获取完数据后,还可以销毁作为代理的iframe。网上也有很多类似的现成代码,有兴趣的可以去找一下。
通过window.name来进行跨域,就是这样子的。

3.4、使用HTML5中新引进的window.postMessage方法在 框架之间发送数据
引用:https://blog.csdn.net/szu_aker/article/details/52314817
window.postMessage(message,targetOrigin)  方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
在发送数据窗口执行:otherWindow.postMessage(msg,origin)
  • otherWindow:表示接受数据的窗口的window对象,包括iframe的contentWindwohe和通过window.open打开的新窗口。
  • msg表示要发送的数据,包扩字符串和对象(ie9以下不支持,可以利用字符串和json互换)
  • origin表示接收的域名。如果不想限定域,可以使用通配符 *。
在接受的窗口监听window的message事件,回掉函数参数接受一个事件对象event,包括的属性有:
  • data:接受的数据
  • origin:发送端的域
  • source:发送端的DOMWindow对象
上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的那种情况,因为每一个框架都有一个window对象。在讨论第二种方法的时候,我们说过,不同域的框架间是可以获取到对方的window对象的,而且也可以使用window.postMessage这个方法。下面看一个简单的示例:
# 利用window.postMessage在框架之间发送数据
1.在父框架页面index.html发送obj对象给远程服务器的wozien.com/test/b.html,该页面是通过iframe加载的,如下
<!DOCTYPE html>
<html>
<head>
	<title>window.postMessage</title>
	
</head>
<body>
	<iframe id="proxy" src="http://wozien.com/test/b.html" onload = "postMsg()" style="display: none" ></iframe>

	<script type="text/javascript">
		var obj = {
			msg: 'this is come from client message!'
		}

		function postMsg (){
			var iframe = document.getElementById('proxy');
			var win = iframe.contentWindow;
			win.postMessage(obj,'http://wozien.com');
		}
		
	</script>
</body>
</html>
2.在远程页面b.html中监听message事件,先通过origin属性判断下数据来源的域是否可信任,加强安全措施。具体代码如下:
<!DOCTYPE html>
<html>
<head>
	<title></title>
	<script type="text/javascript">
		window.onmessage = function(e){
			if(e.origin !== 'http://localhost') return;
			console.log(e.origin+' '+e.data.msg);
		}
	</script>
</head>
<body>
	<p>this is my server</p>
</body>
</html>
使用postMessage来跨域传送数据还是比较直观和方便的,但是缺点是IE6、IE7不支持,所以用不用还得根据实际需要来决定。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值