浏览器对于javascript的同源策略的限制,出现了跨域。
只要协议、域名、端口等不同,就会出现跨域:如下
域名:
主域名不同 http://www.a.com –>http://www.b.com
子域名不同 http://www.m.a.com/index.html –>http://www.n.a.com/test.js
域名和域名ip http://www.a.com/index.html –>http://188.44.77.98/test.js
端口:
http://www.a.com:8080/index.html–> http://www.a.com:9090/test.js
协议:
http://www.a.com:8080/index.html–> https://www.a.com:8080/test.js
其他:
1、端口和协议的不同,只能通过后台来解决
2、localhost和127.0.0.1虽然都指向本机,但也属于跨域
解决跨域有下面几种方法:
- jsonp跨域(JSON with Padding 填充式JSON)
- document.domain
- wndow.name
- postMessage
- 后端修改header
- nginx反向代理
JSONP跨域
只支持get请求。返回的是脚本代码(包含一个函数调用)。
通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
<script> function dosomething(jsonData){ //处理获得的json数据 } </script> <script src="http://xxx.com/test.jsp?callback=dosomething"></script>
知道jsonp跨域的原理后我们就可以用js动态生成script标签来进行跨域操作了,而不用特意的手动的书写那些script标签。如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了。
<script> $.getJSON('http://xxx.com/data.jsp?callback=?',function(jsonData){ //处理获得的JSON数据 }) </script>
document.domain
浏览器中不同域的框架之间是不能进行js的交互操作的。
比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:
<script> function onload(){ var iframe = document.getElementById('iframe') //这里能获取到iframe里的window,但该window的属性和方法是不可用的 var win = iframe.contentWindow //这里获取不到iframe里的document var doc = win.document //这里获取不到window的name var name = win.name </script> <iframe id="iframe" src="http://example.com/b.html" onload="onload()"></iframe>
我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。
在页面 http://www.example.com/a.html 中设置document.domain:
<iframe id="iframe" src="http://example.com/b.html" onload="test()"></iframe> <script> document.domain = 'example.com'; // 设置成主域 function test(){ alert(document.getElementById('iframe').contentWindow) </script>
在页面 http://example.com/b.html 中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:
<script> document.domain = 'example.com'; // 也设置成主域 </script>
window.name
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,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里的代码:
<script>
window.name = '我是页面a.html需要的数据'
</script>
在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。把这个iframe的src设为www.cnblogs.com/data.html就行了。还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。
<script> function getData(){ var iframe = document.getElementById('proxy') iframe.onload = function(){ var data = iframe.contentWindow.name; alert(data) } iframe.src = 'b.html' } </script> <iframe id="proxy" src="http://www.cnblogs.com/data.html" style="display:none;" onload="getData()"></iframe>
window.postMessage
是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源。
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
//这是 页面 http://test.com/a.html 中的代码 <script> function onLoad(){ var iframe = document.getElementById('iframe') var win = iframe.contentWindow;//获取window对象 win.postMessage('我是来自a.html的消息')//向不同域页面发送消息 } </script> <iframe id="iframe" src="http://www.test.com/b.html" onload="onLoad()"></iframe>
//这是 页面http://www.test.com/b.html的代码 <script> window.onmessage = function(e){ //注册message事件来接收消息 e = e || event;//获取事件对象 alert(e.data)//通过data属性得到传送的消息 } </script>
后端修改header
header(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
header(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式
nginx反向代理
Nginx配置
server{ # 监听9099端口 listen 9099; # 域名是localhost server_name localhost; #凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871 location ^~ /api { proxy_pass http://localhost:9871; } }
网上更详细的资源:https://segmentfault.com/a/1190000015597029