如何解决JS跨域问题?
来源:互联网 发布:明解c语言 中级篇 pdf 编辑:程序博客网 时间:2024/06/10 23:40
什么是跨域?
一个域名地址的组成:
当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同的域。
不同域之间相互请求资源,就算 “跨域”。
例如:
a)同一域名,不同协议 不允许通信
http://www.a.com/a.js
https://www.a.com/b.js
b)主域相同,子域不同 不允许通信
http://www.a.com/a.js
http://script.a.com/b.js
c)同一域名,不同二级域名 不允许
http://www.a.com/a.js
http://a.com/b.js
注意:
1)如果是协议和端口造成的跨域问题“前台”是无能为力的
2)在跨域问题上,域仅仅是通过“URL的首部”来识别,而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
例如:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
为什么要解决跨域问题?
跨域问题是由浏览器的同源策略造成的,是浏览器对 JavaScript 施加的安全限制。
所谓同源策略,指的是浏览器对不同源的脚本或者文本的访问方式进行的限制。
从一个域上加载的脚本不允许访问另外一个域的文档属性。
同源策略限制了不同源之间的交互。
如果两个URL的协议、域名和端口都相同,则表示它们同源。
注意:
同源策略限制的不同源之间的交互主要针对的是js中的XMLHttpRequest等请求,下面这些情况是完全不受同源策略限制的:
a)在浏览器中,<script>、<img>、<iframe>、<link>
等标签都可以加载跨域资源,而不受同源限制,但浏览器限制了Javascript不能读写加载的内容。。
b)页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
解决跨域问题的几种方式
1. JSONP跨域访问
Jsonp(JSON with Padding) 是 json 的一种”使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
JSONP也是开发中常见到的内容,在jquery中就有封装,通过ajax请求多带上一个jsonp的参数即可。它的实现原理其实跟ajax没有多少关系。
实现:动态的创建<script>
标签,跨域的地址+需要传送的数据+回调函数为<script>
标签的 src 地址,执行完之后再动态的删除之前创建的<script>
.
回调函数是当响应到来时要放在当前页面被调用的函数。
数据是传入回调函数中的json数据
原理:
1)首先在客户端注册一个回调函数, 然后把回调函数的名字传给服务器;
2)服务器调用这个函数并且将JSON 数据形式作为参数传递,返回给客户端,完成回调。
在页面中,返回的JSON作为参数传入回调函数中,我们通过回调函数来来操作数据。
动态实现JSONP请求
// a.html
<script> function jsonCallback(data) { alert(data.message); } function addScriptTag(src){ var script = document.createElement('script'); script.src = src; document.body.appendChild(script); } window.onload = function(){ addScriptTag("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=apple&callback=jsonCallback"); }</script>
JQuery中$.ajax
的跨域实现:
$.ajax({ async : true, url : "https://api.douban.com/v2/book/search", type : "GET", dataType : "jsonp", // 返回的数据类型,设置为JSONP方式 jsonp : 'callback', //指定一个查询参数名称来覆盖默认的 jsonp 回调参数名 callback jsonpCallback: 'handleResponse', //设置回调函数名 data : { q : "javascript", count : 1 }, success: function(response, status, xhr){ console.log('状态为:' + status + ',状态是:' + xhr.statusText); console.log(response); }});
JQuery中$.getJSON
的跨域实现:
<script type="text/javascript"> $.getJSON("http://crossdomain.com/services.php?callback=?", function(result) { for(var i in result) { alert(i+":"+result[i]);//循环输出a:1,b:2,etc. } }); </script>
缺点:
只能用 get
方式请求,因为是使用 src
来传送数据;
src 对请求的地址没有限制,会出现安全性的问题;
数据中出现中文需要编码。因为通过 url 传参数。
2. document.domain + iframe
适用于:主域相同而子域不同的情况。
实现:
1)在 http://www.a.com/a.html 和 http://script.a.com/b.html 两个文件中分别加上document.domain = ‘a.com’
;
2)在 a.html 文件中创建一个 iframe,去控制 iframe 的contentDocument。
//在www.a.com/a.html中
document.domain = 'a.com';var ifr = document.createElement('iframe');ifr.src = 'http://script.a.com/b.html';ifr.style.display = 'none';document.body.appendChild(ifr);ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; // 在这里操纵b.html(doc)};
//在 script.a.com/b.html 中
document.domain = 'a.com';
注意:
domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。
缺点:
1)安全性。当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
2)如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。
3.动态创建script
script 标签不受同源策略的限制。因此 ,通过创建script节点的方法来实现完全跨域的通信。
function loadScript(url, func) { var head = document.head || document.getElementByTagName('head')[0]; var js = document.createElement('script'); js.src = url;//判断script节点加载完毕 js.onload = js.onreadystatechange = function(){ if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){ func();// callback在此处执行 js.onload = js.onreadystatechange = null; } }; head.insertBefore(script, 0);}
ie只能通过script的readystatechange属性,其它浏览器是script的load事件。
4. location.hash + iframe
原理:利用location.hash来进行传值。
例如,http://a.com#helloword 中的 ‘#helloworld’ 就是 location.hash,改变hash并不会导致页面刷新,所以可以利用hash值来进行数据传递,当然数据容量是有限的。
假设域名a.com
下的文件cs1.html
要和cnblogs.com
域名下的cs2.html
传递信息:
1) cs1.html首先自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面;
2) cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据;
3) 同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值。
注:由于两个页面不在同一个域下,IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe
a.com下的文件cs1.html文件:
function startRequest(){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo'; document.body.appendChild(ifr);}function checkHash() { try { var data = location.hash ? location.hash.substring(1) : ''; if (console.log) { console.log('Now the data is '+data); } } catch(e) {};}setInterval(checkHash, 2000);
cnblogs.com域名下的cs2.html:
//模拟一个简单的参数处理操作switch(location.hash){ case '#paramdo': callBack(); break; case '#paramset': //do something…… break;}function callBack(){ try { parent.location.hash = 'somedata'; } catch (e) { // ie、chrome的安全机制无法修改parent.location.hash, // 所以要利用一个中间的cnblogs域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意该文件在"a.com"域下 document.body.appendChild(ifrproxy); }}
5. window.name + iframe
window对象name属性的特征:
在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
并且可以支持非常长的 name 值(2MB)。
实现:
1)在应用页面(a.com/app.html)中创建一个iframe,把其src指向数据页面(b.com/data.html)
2)在应用页面(a.com/app.html)中,监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理文件(代理文件和应用页面在同一域下,所以可以相互通信)
3)获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
//a.com/app.html
<script type="text/javascript"> var state = 0, iframe = document.createElement('iframe'), iframe.src = 'http://b.com/data.html'; loadfn = function() { if (state === 1) { var data = iframe.contentWindow.name; // 读取数据 alert(data); //弹出'I was there!' iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if (state === 0) { state = 1; // 设置的代理文件 iframe.contentWindow.location = "http://a.com/proxy.html"; } }; if (iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe);</script>
//data.html
<script type="text/javascript"> window.name = 'I was there!'; // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右 // 数据格式可以自定义,如json、字符串</script>
销毁
<script> iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe);</script>
参考:
window.name实现的跨域数据传输
使用 window.name 解决跨域问题
利用window.name+iframe跨域获取数据详解
6. HTML5 的 window.postMessage 方法
window.postMessage(message,targetOrigin) 方法是 html5 新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源。
otherWindow.postMessage(message,targetOrigin)
otherWindow——指要接收消息的那一个window对象。
message——要发送的数据。string类型
targetOrigin——用于限制接收消息的window对象,“*”表示不作限制
otherWindow 通过监听自身的 message 事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
a.com/index.html中的代码:
<iframe id="ifr" src="b.com/index.html"></iframe><script>window.onload = function() { var ifr = document.getElementById('ifr'); var targetOrigin = 'http://b.com'; // 若写成'http://b.com/c/proxy.html'效果一样// 若写成'http://c.com'就不会执行postMessage了 ifr.contentWindow.postMessage('I come from a.com!', targetOrigin);};</script>
b.com/index.html中的代码:
<script>window.addEventListener('message', function(event){ // 通过origin属性判断消息来源地址 if (event.origin == 'http://a.com') { alert(event.data); // 弹出"I come from a.com!" } }, false);</script>
- 如何解决js跨域问题
- 如何解决js跨域问题
- 如何解决js跨域问题
- 如何解决JS跨域问题?
- 解决js跨域问题
- 解决js跨域问题
- 解决js跨域问题
- 解决js跨域问题
- js解决跨域问题
- 【js】Jsonp解决js跨域问题
- 如何解决跨域问题
- 如何解决跨域问题?
- 如何解决跨域问题?
- 如何解决跨域问题?
- 如何解决node.js中fs.rename文件跨域问题
- 如何解决radio的js验证问题
- 简单的解决js跨域问题
- requirejs解决js跨域问题
- HDU 6055-Regular polygon (判断n个整数点能够成多少个正多边形)
- 随机数的产生
- MailBee.NET Objects撰写邮件教程(一):添加和删除自定义标题
- tensorflow mnist tensorboard tensorflow版本原因导致可视化出错
- ESP8266WIFI模块和电脑通信
- 如何解决JS跨域问题?
- 使用蒲公英给iOS应用做内测
- LNK2019无法解析的外部符号 "public: __thiscall cv::SURF::SURF(double,int,int,bool,bool)" (??0SURF@cv@@QAE@NHH_
- [暴力]HDU 4473——Exam
- win10下的VS2010+Qt5.3.1的安装、路径配置、项目配置及各种出错记录
- 好题-HDU5135-壮压|贪心-Little Zu Chongzhi's Triangles
- Linux C编程学习笔记(7):线程控制
- Java学习之网络编程
- js选择当前元素位置