跨域
一、什么是跨域?
当一个请求url的协议、域名、端口三者之间的任意一个与当前页面url不同即为跨域。
当前页面url | 被请求页面url | 是否跨域 | 原因 |
---|---|---|---|
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(协议、域名、端口号相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 | 协议不同(http/https) |
http://www.test.com/ | http://www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同(www/blog) |
http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 | 端口号不同(8080/7001) |
二、为什么会出现跨域?
出于浏览器的*同源策略限制**。
同源策略(Same Orgin Policy)是一种约定,它是浏览器核心也最基本的安全功能,它会阻止一个域的js脚本和另外一个域的内容进行交互,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。。所谓同源(即在同一个域)就是两个页面具有相同的协议(protocol)、主机(host)和端口号(port)。
三、非同源限制
- 无法读取非同源网页的cookie、localstorage和indexedDB
- 无法接触非同源网页的DOM和js对象
- 无法向非同源地址发送Ajax请求
四、跨域解决方法
(1)document.domain + iframe跨域
(2)location.hash + iframe跨域
(3)window.name + iframe跨域
(4)postMessage跨域
(5)通过jsonp跨域
(6)跨域资源共享(CORS)
(7)Nginx反向代理
(8)nodejs中间件地代理跨域
(9)WebSocket协议跨域
一般来说,前三个是iframe的跨域;窗口之间JS跨域postMessage;简单的跨域请求jsonp即可,复杂的用cors;开发环境下接口跨域用nginx反向代理或node中间件比较方便。
4.1、document.domain + iframe跨域
该方法仅限于主域相同,子域不同的跨域应用场景。
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
(1)父窗口:http://www.domain.com/a.html
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script>
(2)子窗口:http://child.domain.com/b.html
<script> document.domain = 'domain.com'; // 获取父窗口中变量 alert('get js data from parent ---> ' + window.parent.user); </script>
4.2、location.hash + iframe跨域
实现场景:A域(a.html)-> B域(b.html)-> A域(c.html)。a与b不同域只能单向通信,但c与a同域,所以c可以通过parent.parent访问a页面所有对象。
实现原理:三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
(1)a.html:http://www.domain1.com/a.html
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 向b.html传hash值 setTimeout(function() { iframe.src = iframe.src + '#user=admin'; }, 1000); // 开放给同域c.html的回调方法 function onCallback(res) { alert('data from c.html ---> ' + res); } </script>
(2)b.html:http://www.domain2.com/b.html
<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 监听a.html传来的hash值,再传给c.html window.onhashchange = function () { iframe.src = iframe.src + location.hash; }; </script>
(3)c.html:(http://www.domain1.com/c.html)
<script> // 监听b.html传来的hash值 window.onhashchange = function () { // 再通过操作同域a.html的js回调,将结果传回 window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', '')); }; </script>
4.3、window.name + iframe跨域
实现原理:通过iframe的src属性由外域转向本地域,跨域数据即由if让么的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时又是安全操作。
window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
(1)a.html:http://www.domain1.com/a.html
var proxy = function(url, callback) { var state = 0; var iframe = document.createElement('iframe'); // 加载跨域页面 iframe.src = url; // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name iframe.onload = function() { if (state === 1) { // 第2次onload(同域proxy页)成功后,读取同域window.name中数据 callback(iframe.contentWindow.name); destoryFrame(); } else if (state === 0) { // 第1次onload(跨域页)成功后,切换到同域代理页面 iframe.contentWindow.location = 'http://www.domain1.com/proxy.html'; state = 1; } }; document.body.appendChild(iframe); // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问) function destoryFrame() { iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } }; // 请求跨域b页面数据 proxy('http://www.domain2.com/b.html', function(data){ alert(data); });
(2)proxy.html:http://www.domain1.com/proxy....
中间代理页,与a.html同域,内容为空即可。
(3)b.html:http://www.domain2.com/b.html
<script> window.name = 'This is domain2 data!'; </script>
4.4、postMessage跨域
实现原理:postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可以用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间的消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
用法:postMessage(data,origin)方法接受两个参数
- data:html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringfy()序列化。
- origin:协议+主机+端口号,也可设置为‘*’,表示可以传递给任意窗口,如果要制定和当前窗口同源的话设置为‘/’。
(1)a.html:http://www.domain1.com/a.html
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; // 向domain2传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); }; // 接受domain2返回数据 window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script>
(2)b.html:http://www.domain2.com/b.html
<script> // 接收domain1的数据 window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); console.log(e.source); // e.source 发送消息的窗口 console.log(e.origin); // e.origin 消息发向的网址 console.log(e.data); // e.data 发送的消息 var data = JSON.parse(e.data); if (data) { data.number = 16; // 处理后再发回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
4.5、通过jsonp跨域
实现原理:通常为了减轻web服务器的负载,我们把js、css、图片等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许。我们可以利用
本文转自:https://www.cnblogs.com/jsunwang/p/13519163.html