浅谈浏览器端JavaScript跨域解决方法
由于安全的原因,浏览器做了很多方面的工作,由此也就引入了一系列的跨域问题,需要注意的是:
跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是 crsf
跨站攻击原理,请求是发送到了后端服务器无论是否跨域!注意:有些浏览器不允许从HTTPS的域跨域访问HTTP,比如Chrome和Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例
1. JSONP
JSONP
的全称是 "JSON With Padding", 词面意思上理解就是 "填充式的JSON"。它不是一个新鲜的东西,隶属于 JSON
的一种使用方法,或者说是一种使用模式,可以解决一些常见的浏览器端网页跨域问题。
正如他的名称一样,它是指被包含在调用函数中的JSON,比如这样:
callback({"Name": "小明", "Id" : 1823, "Rank": 7})
由于 jQuery
的一些原因,使得 JSONP
常常与 Ajax
混淆。实际上,他们没有任何关系。
由于浏览器的同源策略,使得在网页端出现了这个“跨域”的问题,然而我们发现,所有的 src
属性并没有受到相关的限制,比如 img
/ script
等。
JSONP
的原理就要从 script
说起。script
可以执行其他域的js
函数,比如这样:
a.html
...
<script>function callback(data) {console.log(data.url)}
</script><script src='b.js'></script>
...b.js
callback({url: 'http://www.rccoder.net'})
显然,上面的代码是可以执行的,并且可以在console里面输出http://www.rccoder.net
利用这一点,假如b.js里面的内容不是固定的,而是根据一些东西自动生成的, 嗯,这就是JSONP的主要原理了。回调函数+数据就是 JSON With Padding
了,回调函数用来响应应该在页面中调用的函数,数据则用来传入要执行的回调函数。
至于这个数据是怎么产生的,说粗鲁点无非就是字符串拼接了。
简单总结一下: Ajax 是利用 XMLHTTPRequest 来请求数据的,而它是不能请求不同域上的数据的。但是,在页面上引用不同域的 js 文件却是没有任何问题的,这样,利用异步的加载,请求一个 js 文件,而这个文件的内容是动态生成的(后台语言字符串拼接出来的),里面包含的是 JSON With Padding(回调函数+数据),之前写的那个函数就因为新加载进来的这段动态生成的 js 而执行,也就是获取到了他要获取的数据。
重复一下,在一个页面中,a.html这样写,得到 UserId 为 1823 的信息:
a.html...
src="http://server2.example.com/RetrieveUser?UserId=1823&callback=parseResponse">
...
请求这个地址会得到一个可以执行的 JavaScript。比如会得到:
parseResponse({"Name": "小明", "Id" : 1823, "Rank": 7})
这样,a.html里面的 parseResponse()
这个函数就能执行并且得到数据了。
等等,jQuery到底做了什么:
jQuery 让 JSONP 的使用API和Ajax的一模一样:
$.ajax({method: 'jsonp',url: 'http://server2.example.com/RetrieveUser?UserId=1823',success: function(data) {console.log(data)}
})
之所以可以这样是因为 jQuery 在背后倾注了心血,它会在执行的时候生成函数替换callback=dosomthing
,然后获取到数据之后销毁掉这个函数,起到一个临时的代理器作用,这样就拿到了数据。
JSONP 的后话:
JSONP的这种实现方式不受同源策略的影响,兼容性也很好;但是它之支持 GET 方式的清楚,只支持 HTTP 请求这种特殊的情况,对于两个不同域之间两个页面的互相调用也是无能为力。
2. CORS
XMLHttpRequest
的同源策略看起来是如此的变态,即使是同一个公司的产品,也不可能完全在同一个域上面。还好,网络设计者在设计的时候考略到了这一点,可以在服务器端进行一些定义,允许部分网络访问。
CORS 的全称是 Cross-Origin Resource Sharing,即跨域资源共享。他的原理就是使用自定义的 HTTP 头部,让服务器与浏览器进行沟通,主要是通过设置响应头的 Access-Control-Allow-Origin
来达到目的的。这样,XMLHttpRequest 就能跨域了。
值得注意的是,正常情况下的 XMLHttpRequest 是只发送一次请求的,但是跨域问题下很可能是会发送两次的请求(预发送)。
更加详细的内容可以参见:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
CORS 的后话:
相比之下,CORS 就支持所有类型的 HTTP 请求了,但是在兼容上面,往往一些老的浏览器并不支持 CORS。
Desktop:
浏览器 | 版本 |
---|---|
Chrome | 4 |
Firefox (Gecko) | 3.5 |
Internet Explorer | 8 (via XDomainReques) 10 |
Opera | 12 |
Safari | 4 |
Mobile:
设备 | 版本 |
---|---|
Android | 2.1 |
Chrome for Android | yes |
Firefox Mobile (Gecko) | yes |
IE Mobile | ? |
Opera Mobile | 12 |
Safari Mobile | 3.2 |
3. window.name
window.name 在一个窗口(标签)的生命周期之内是共享的,利用这点就可以传输一些数据。
除此之外,结合 iframe 还能实现更加强大的功能:
需要3个文件: a/proxy/b
a.html<script type="text/javascript">var state = 0, iframe = document.createElement('iframe'),loadfn = function() {if (state === 1) {var data = iframe.contentWindow.name; // 读取数据alert(data); //弹出'I was there!'} else if (state === 0) {state = 1;iframe.contentWindow.location = "http://a.com/proxy.html"; // 设置的代理文件} };iframe.src = 'http://b.com/b.html';if (iframe.attachEvent) {iframe.attachEvent('onload', loadfn);} else {iframe.onload = loadfn;}document.body.appendChild(iframe);
</script>
b.html<script type="text/javascript">window.name = 'I was there!'; // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右// 数据格式可以自定义,如json、字符串
</script>
proxy 是一个代理文件,空的就可以,需要和 a 在同一域下
4. document.domain
在不同的子域 + iframe交互的时候,获取到另外一个 iframe 的 window对象是没有问题的,但是获取到的这个window的方法和属性大多数都是不能使用的。
这种现象可以借助document.domain
来解决。
example.com<iframe id='i' src="1.example.com" onload="do()"></iframe>
<script>document.domain = 'example.com';document.getElementById("i").contentWindow;
</script>
1.example.com<script>document.domain = 'example.com';
</script>
这样,就可以解决问题了。值得注意的是:document.domain
的设置是有限制的,只能设置为页面本身或者更高一级的域名。
document.domain的后话:
利用这种方法是极其方便的,但是如果一个网站被攻击之后另外一个网站很可能会引起安全漏洞。
5.location.hash
这种方法可以把数据的变化显示在 url 的 hash 里面。但是由于 chrome 和 IE 不允许修改parent.location.hash 的值,所以需要再加一层。
a.html 和 b.html 进行数据交换。
a.htmlfunction startRequest(){var ifr = document.createElement('iframe');ifr.style.display = 'none';ifr.src = 'http://2.com/b.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);
b.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,// 所以要利用一个中间域下的代理iframevar ifrproxy = document.createElement('iframe');ifrproxy.style.display = 'none';ifrproxy.src = 'http://3.com/c.html#somedata'; // 注意该文件在"a.com"域下document.body.appendChild(ifrproxy);}
}
c.html//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
这样,利用中间的 c 层就可以用 hash 达到 a 与 b 的交互了。
6.window.postMessage()
这个方法是 HTML5 的一个新特性,可以用来向其他所有的window对象发送消息。需要注意的是我们必须要保证所有的脚本执行完才发送MessageEvent,如果在函数执行的过程中调用了他,就会让后面的函数超时无法执行。
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
参考资料
http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html
http://www.cnblogs.com/rainman/archive/2011/02/21/1960044.html
硬广
原文地址: https://github.com/rccoder/blog/issues/5
欢迎 star 仓库~
浅谈浏览器端JavaScript跨域解决方法相关推荐
- JavaScript跨域解决方法大全
跨域的定义:JavaScript出于安全性考虑,同源策略机制对跨域访问做了限制.域仅仅是通过"URL的首部"字符串进行识别,"URL的首部"指window.lo ...
- jquery、javascript实现(get、post两种方式)跨域解决方法
jquery.javascript实现(get.post两种方式)跨域解决方法 一.实现get方式跨域请求数据 浏览器端 <script> $(document).ready(fun ...
- 跨域解决方法——jsonp原理
跨域解决方法--jsonp原理 一个域名地址的组成: 当协议.子域名.主域名.端口号任意一个不相同时,都算作不同域,不同域之间相互请求资源,就算做"跨域".由于浏览器同源策略的限制 ...
- React项目中请求跨域解决方法
React项目中请求跨域解决方法 今天经理给我了一个React项目地址,让我拉下来并跑起来,拉下来运行起来后,发现所有的请求都失败了,并且都是由于跨域问题导致的.花了点时间,解决了这个问题,在这里记录 ...
- Js跨域解决方法总结
本文转载自网易博客 出于安全性的考虑,在AJAX应用中,浏览器通常都会限制跨域提交数据.但由于经常和其他部门有接口对接的业务需求,需要跨域获取数据. IE对于跨域访问的处理是,弹出警告框,提醒用户.如 ...
- withCredentials--相同主域跨域解决方法
主域相同,而子域不同,存在跨域问题.在高版本浏览器下,可以设置withCredentials来解决. xhrFields: {withCredentials: true} 低版本浏览器标准IE6. I ...
- frame页面地址转向跨域解决方法
mainFrame.htm下有两个iframe,左边是left.aspx,frame名是leftFrame,右边名是是mainFrame left.aspx包含了很多的连接,linka和linkc连到 ...
- 几种常见的跨域解决方法
前言 由于浏览器的同源策略,当我们请求网络资源时,所在页面的url中的协议,端口,域名其中一个与请求资源的url不同,都会出现跨域的问题.但是浏览器不能没有这个策略,这样会很危险,像csrf,xss攻 ...
- php 播放多个音乐,meting 音乐播放插件多域名跨域解决方法
换了模板使用了meting音乐播放插件由于我解析了两个域名 一个是顶级域名nnnuo.com,还有个www二级域名. 但是meting插件在后台设置云解析地址的时候只要是使用其中一个域名另外一个域名访 ...
- 关于http和https淘宝支付宝跨域解决方法研究
关于http和http跨域淘宝解决方式研究: http://buyer.trade.taobao.com/trade/pay.htm?spm=a1z01.2.3.4.0.wZAGp9&bizO ...
最新文章
- NVIDIA安培架构
- Linux network source code
- java8 lambda python_java8里面lambda的stream()用法讲解
- concat合并的数组会有顺序么_JS数组 Array
- Valid signing identity not found解决办法(原有IDP私钥丢失)
- Expression Blend实战开发技巧 第四章 从最常用ButtonStyle开始 - PathButton
- 推荐系统有什么危害?
- html----选项卡自动切换以及鼠标悬停时停止(js)
- 计算机二级报名省市,计算机二级报名通知:全国19省市报名时间及考试安排!...
- [软件更新]影拓三代 PTZ系列 系列 驱动程序 V6.1.1-3{链接已修复}
- Linux安装与配置SSH服务
- Arduino学习之第一篇
- word多级目录设置和自动生成目录
- win7无法连接打印机拒绝访问_win7添加打印机提示windows无法连接到打印机怎么解决...
- 第九章——有ISI和AWGN信道的最佳接收机
- 一个IT从业者的课外读物___养生锻炼篇
- 动词ing形式的5种用法_加ing的形式有哪几种?怎样用
- 学Python真的好找工作吗?工作多年的程序员为你解答
- 怎么修改路由器地址的默认IP
- 数据结构<1>时空复杂度详解
热门文章
- ASP.NET MVC3 Model验证总结
- 关于SRTM精度的讨论[文摘]
- Gaze Estimation学习笔记(1)-Appearance-Based Gaze Estimation in the Wild
- 06 Python爬虫之Re(正则表达式)库
- 二 Djano模型层之模型字段选项
- A Translation for Quaternion 一篇对四元数的翻译
- php 查询出来的字段名全是小写或者大写
- 网站自动适配技术实现原理
- 转----cer文件和pfx文件的区别
- gcc 的宏替换 __stringify