JS跨域总结


Posted in Javascript onAugust 30, 2012

javascript跨域有两种情况:
1、基于同一父域的子域之间,如:a.c.com和b.c.com
2、基于不同的父域之间,如:www.a.com和www.b.com
3、端口的不同,如:www.a.com:8080和www.a.com:8088
4、协议不同,如:http://www.a.com和https://www.a.com
对于情况3和4,需要通过后台proxy来解决,具体方式如下:
a、在发起方的域下创建proxy程序
b、发起方的js调用本域下的proxy程序
c、proxy将请求发送给接收方并获取相应数据
d、proxy将获得的数据返回给发起方的js
发起方页面代码如下:

<form id="form1" runat="server"> 
<div> 
<input type="text" id="txtSrc" value="http://www.gzsums.edu.cn/webclass/html/html_design.html" style="width: 378px" /> 
<input id="btnProxy" type="button" value="通过Proxy获取数据" onclick="GetDataFromProxy();" /><br /> 
<br /> 
<br /> 
</div> 
<div id="divData"></div> 
</form> 
</body> 
<script language="javascript" type="text/javascript"> 
function GetDataFromProxy() { 
var src = document.getElementById('txtSrc').value; 
var request = null; 
if (window.XMLHttpRequest) { 
request = new XMLHttpRequest(); 
} 
else if (window.ActiveXObject) { 
request = new ActiveXObject("Microsoft.XMLHTTP"); 
} 
request.onreadystatechange = function() { 
var ready = request.readyState; 
var data = null; 
{ 
if (ready == 4) { 
data = request.responseText; 
document.getElementById('divData').innerHTML = data; 
} 
else { 
document.getElementById('divData').text = "Loading"; 
} 
} 
} 
var url = "Proxy.ashx?src=" + escape(src); 
request.open("get",url,false); 
request.send(null); 
} 
</script>

发起方Proxy代码如下:
using System.Data; 
using System.Linq; 
using System.Web; 
using System.Web.Services; 
using System.Web.Services.Protocols; 
using System.Xml.Linq; 
using System.IO; 
using System.Net; 
using System.Text; 
namespace WebApplication1 
{ 
/// <summary> 
/// Summary description for $codebehindclassname$ 
/// </summary> 
[WebService(Namespace = "http://tempuri.org/")] 
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
public class Proxy : IHttpHandler 
{ 
const int BUFFER_SIZE = 8 * 1024; 
public void ProcessRequest(HttpContext context) 
{ 
context.Response.ContentType = "text/plain"; 
string src = context.Request["src"]; 
WebRequest wr = WebRequest.Create(src); 
WebResponse wres = wr.GetResponse(); 
Encoding resEncoding = System.Text.Encoding.GetEncoding("gb2312"); 
StreamReader sr = new StreamReader(wres.GetResponseStream(), resEncoding); 
string html = sr.ReadToEnd(); 
sr.Close(); 
wres.Close(); 
context.Response.Write("<br/><br/><br/><br/>"); 
context.Response.Write(html); 
} 
public bool IsReusable 
{ 
get 
{ 
return false; 
} 
} 
} 
}

而情况1和2除了通过后台proxy这种方式外,还可以有7种办法来解决:
1、document.domain+iframe(只能解决情况1):
a、在发起方页面和接收方页面设置document.domain,并将值设为父域的主域名(window.location.hostname)
  b、在发起方页面创建一个隐藏的iframe,iframe的源是接收方页面
c、根据浏览器的不同,通过iframe.contentDocument || iframe.contentWindow.document来获得接收方页面的内容
d、通过获得的接收方页面的内容来与接收方进行交互
这种方法有个缺点,就是当一个域被攻击时,另一个域会有安全漏洞出现。
发起方页面代码如下:
<body> 
<div> 
<input type="text" id="txtSrc" value="http://b.a.com/DomainTest2.htm" style="width: 378px" /> 
<input id="btnDomain" type="button" value="通过Domain获取数据" onclick="GetDataFromDomain();" /><br /> 
<br /> 
<br /> 
</div> 
<div id="divData"></div> 
</body> 
<script language="javascript" type="text/javascript"> 
document.domain = 'a.com'; 
var src = document.getElementById('txtSrc').value; 
var ifr = document.createElement('iframe'); 
ifr.src = src; 
ifr.style.display = 'none'; 
document.body.appendChild(ifr); 
function GetDataFromDomain() { 
var doc = ifr.contentDocument || ifr.contentWindow.document; 
alert(doc.getElementById("data").value); 
} 
</script>

接收方页面代码如下:
<body> 
<input type="hidden" id="data" value="Cross Domain" style="width: 378px" /> 
</body> 
<script language="javascript" type="text/javascript"> 
document.domain = 'a.com'; 
</script>

2、动态创建script:
  a、在发起方页面动态加载一个script,script的URL指向接收方的一个处理地址(后台),该地址返回的javascript方法会被执行,另外URL中可以传入一些参数,该方法只支持GET方式提交参数。
  b、加载的script可以在调用跨域js方法后再做一些自己的处理
发起方页面的代码如下:
<head> 
<title>Script Test</title> 
<script language="javascript" type="text/javascript"> 
function load_script(callback){ 
var head = document.getElementsByTagName('head')[0]; 
var script = document.createElement('script'); 
var src = document.getElementById('txtSrc').value; 
script.type = 'text/javascript'; 
script.src = src; 
//借鉴了jQuery的script跨域方法 
script.onload = script.onreadystatechange = function(){ 
if((!this.readyState||this.readyState === "loaded"||this.readyState === "complete")){ 
callback && callback(); 
// Handle memory leak in IE 
script.onload = script.onreadystatechange = null; 
if ( head && script.parentNode ) { 
head.removeChild( script ); 
} 
} 
}; 
// Use insertBefore instead of appendChild to circumvent an IE6 bug. 
head.insertBefore( script, head.firstChild ); 
} 
</script> 
</head> 
<body> 
<input type="text" id="txtSrc" value="http://www.b.com/scripttest.aspx" style="width: 378px" /> 
<input type="button" value="通过动态创建script标签来获取数据" onclick="load_script(function(){alert('动态加载script标签成功')})"/> 
</body>

接收方服务器端代码如下:
protected void Page_Load(object sender, EventArgs e) 
{ 
Response.Clear(); 
Response.ContentType = "application/x-javascript"; 
Response.Write(String.Format(@"alert('{0}');", DateTime.Now)); 
Response.End(); 
}

3、location.hash+iframe:
  a、发起方创建一个隐藏的iframe,iframe的源指向接收方的页面,并通过接收方页面的hash值来传送数据
  b、发起方创建一个定时器,定时检查自己的location.hash并作相应的处理
c、接收方创建一个隐藏的iframe,iframe的源指向发起方所在域的一个代理页面,并将接收方根据发起方传入的数据而处理后的数据通过代理页面的hash值来传送
d、接收方创建一个定时器,定时检查自己的location.hash并作相应的处理
e、代理页面创建一个定时器,定时检查自己的location.hash并同步更新发起方页面的hash值
www.a.com/a.html#aaa,其中#aaa就是location.hash值
发起方页面代码如下:
<body> 
<div> 
<input type="text" id="txtSrc" value="1" style="width: 378px" /> 
<input id="btnAddHash" type="button" value="添加Hash值" onclick="addHash();" /> 
<iframe id="ifr1" style="display:none"></iframe> 
</div> 
</body> 
<script language="javascript" type="text/javascript"> 
function addHash() { 
var src = document.getElementById('txtSrc').value; 
if (src.length > 0) { 
changeHash(src); 
} 
} 
function changeHash(src) { 
if (document.getElementById('ifr1')) { 
var ifr = document.getElementById('ifr1'); 
ifr.src = 'http://www.b.com/Test/HashTest2.htm#' + src; 
} 
else { 
var ifr = document.createElement('iframe'); 
ifr.setAttribute('id', 'ifr1'); 
ifr.src = 'http://www.b.com/Test/HashTest2.htm#' + src; 
ifr.style.display = 'none'; 
document.body.appendChild(ifr); 
} 
} 
function checkHash() { 
if (location.hash && location.hash.length > 1) { 
changeHash(location.hash.substring(1)); 
} 
} 
setInterval(checkHash, 2000); 
</script>

接收方页面代码如下:
<body> 
<iframe id="ifr2" style="display:none"></iframe> 
</body> 
<script language="javascript" type="text/javascript"> 
function checkHash() { 
if (location.hash && location.hash.length > 1) { 
var hashData = location.hash.substring(1); 
var ifr = null; 
if (document.getElementById('ifr2')) { 
ifr = document.getElementById('ifr2'); 
} 
else { 
ifr = document.createElement('iframe'); 
ifr.setAttribute('id', 'ifr2'); 
ifr.style.display = 'none'; 
document.body.appendChild(ifr); 
} 
switch (hashData) { 
case '1': 
alert('One'); 
if (ifr) { 
ifr.src = 'http://www.a.com/test/HashTest3.htm#2'; 
} 
break; 
case '2': 
alert('Two'); 
if (ifr) { 
ifr.src = 'http://www.a.com/test/HashTest3.htm#1'; 
} 
break; 
default: 
break; 
} 
} 
} 
setInterval(checkHash, 2000); 
</script>

发起方域下的代理页面代码如下:
<body></body> 
<script language="javascript" type="text/javascript"> 
function checkHash() { 
if (parent && parent.parent && parent.parent.location && self.location.hash.length > 1) { 
parent.parent.location.hash = self.location.hash.substring(1); 
} 
} 
setInterval(checkHash, 500); 
</script>

4、window.name:
a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面
b、接收方在自己页面通过script将需要传送的数据放入window.name里
c、发起方在iframe的onload方法里将iframe的源改为和自己在同一个域下的代理页面(因为只能是同一个域下才能访问window.name的值)
d、获取window.name的值(虽然iframe的源改变了,但是window.name的值不会变)
window.name的值差不多可以有2MB大小
发起方页面代码如下:
<body> 
<div> 
<input id="btnName" type="button" value="通过window.name获取数据" onclick="getData();" /> 
<iframe id="ifr1" style="display:none" src="http://www.b.com/Test/NameTest2.htm"></iframe> 
</div> 
</body> 
<script language="javascript" type="text/javascript"> 
var ischanged = false; 
function changeSrc() { 
if (document.getElementById('ifr1')) { 
var ifr = document.getElementById('ifr1'); 
if (!ischanged) { 
ischanged = true; 
ifr.contentWindow.location = 'http://www.a.com/Test/NameTest3.htm'; 
} 
else { 
var data = ifr.contentWindow.name; 
alert(data); 
} 
} 
else { 
var ifr = document.createElement('iframe'); 
ifr.setAttribute('id', 'ifr1'); 
ifr.src = 'http://www.b.com/Test/NameTest2.htm'; 
ifr.style.display = 'none'; 
document.body.appendChild(ifr); 
} 
} 
function getData() { 
setInterval(changeSrc, 2000); 
} 
</script>

接收方页面代码如下:
<body></body> 
<script language="javascript" type="text/javascript"> 
window.name = 'NameTest2'; 
</script>

发起方域下的代理页面代码如下:
<body></body>
(其实什么都不用写)
5、HTML5的postMessage
a、receiverWindow.postMessage(msg, targetOrigin),receiverWindow就是对接收消息的window的引用,可以是iframe的contentWindow/window.open的返回值/window.frames中的一个;msg就是要发送的消息,string类型;targetOrigin用于限制receiverWindow的URI,包括主域名和端口,使用“*”表示无限制,但是为了安全起见还是需要设置下,以防把消息发送给恶意的网站,如果targetOrigin的URI和receiverWindow的不符,则放弃发送消息。
b、接收方通过message事件来获得消息,并且通过event.origin的属性来验证发送方并通过event.data来获得传送的消息内容,event.source来获得发送方的window对象
发起方页面代码如下:
<body> 
<div> 
<input id="btnPostMessage" type="button" value="通过PostMessage获取数据" onclick="getData();" /> 
<iframe id="ifr" style="display:none" src="http://www.b.com/Test/PostMessageTest2.htm"></iframe> 
</div> 
</body> 
<script language="javascript" type="text/javascript"> 
function getData() { 
var ifr = document.getElementById('ifr'); 
var targetOrigin = 'http://www.b.com'; 
if (ifr.contentWindow.postMessage) { 
ifr.contentWindow.postMessage('PostMessageTest2', targetOrigin); 
} 
} 
</script>

接收方页面代码如下:
<body></body> 
<script language="javascript" type="text/javascript"> 
window.addEventListener('message', function(event) { 
if (event.origin == 'http://www.a.com') { 
alert(event.data); 
alert(event.source); 
} 
}, false); 
</script>

6、window.opener(适用于IE6、7,也就是operner hack方法,不过貌似现在已经不管用了,只要打过微软的安全补丁.kb2497640就不能用了)
  a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面
b、发起方页面通过iframe.contentWindow.opener = {a: function(params){...}, b: function(params){...} ...}来定义可被接收方调用的方法
c、接收方页面通过window.opener.a/window.opener.b来调用发起方定义的方法
d、接收方页面通过parent.opener = {c: function(params){...}, d: function(params){...} ...}来定义可被发起方调用的方法
e、发起方页面通过opener.c/opener.d来调用接收方定义的方法
其实原理就是重置opener对象
发起方页面代码如下:
<body> 
<iframe id="ifr" src="http://www.b.com/test/OpenerTest2.htm" style="display:none"></iframe> 
</body> 
<script language="javascript" type="text/javascript"> 
var ifr = document.getElementById('ifr'); 
ifr.contentWindow.opener = { a: function(msg) { alert('我调用了a方法获得了消息:' + msg); } } 
</script>

接收方页面代码如下:
<body> 
</body> 
<script language="javascript" type="text/javascript"> 
window.opener.a('aaa'); 
</script>

7、window.navigator(适用于IE6、7,貌似现在还能用,还没被补丁掉)
a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面
b、发起方页面通过window.navigator.a = function(params){...}; window.navigator.b = function(params){...}; 来定义被接收方调用的方法
c、接收方页面通过window.navigator.a(params); window.navigator.b(params);来调用发起方定义的方法
d、接收方页面通过window.navigator.c = function(params){...}; window.navigator.d = function(params){...}; 来定义被发起方调用的方法
e、发起方页面通过window.navigator.c(params); window.navigator.d(params);来调用接收方定义的方法
发起方页面代码如下:
<body> 
<iframe id="ifr" src="http://www.b.com/test/NavigatorTest2.htm" style="display:none"></iframe> 
</body> 
<script language="javascript" type="text/javascript"> 
window.navigator.a = function(msg) { alert('我调用了a方法获得了消息:' + msg); } 
window.navigator.b = function(msg) { alert('我调用了b方法获得了消息:' + msg); } 
setInterval(function() { window.navigator.c('ccc'); }, 2000); 
setInterval(function() { window.navigator.d('ddd'); }, 2000); 
</script>

接收方页面代码如下:
<body> 
</body> 
<script language="javascript" type="text/javascript"> 
window.navigator.c = function(msg) { alert('我调用了c方法获得了消息:' + msg); } 
window.navigator.d = function(msg) { alert('我调用了d方法获得了消息:' + msg); } 
setInterval(function() { window.navigator.a('aaa'); }, 2000); 
setInterval(function() { window.navigator.b('bbb'); }, 2000); 
</script>
Javascript 相关文章推荐
Javascript日期对象的dateAdd与dateDiff方法
Nov 18 Javascript
目前流行的JavaScript库的介绍及对比
Sep 29 Javascript
jQuery对象的length属性用法实例
Dec 27 Javascript
jQuery实现两款有动画功能的导航菜单代码
Sep 16 Javascript
Jqgrid之强大的表格插件应用
Dec 02 Javascript
基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用
Dec 16 Javascript
给easyui的datebox控件添加清空按钮的实现方法
Nov 09 Javascript
JavaScript队列函数和异步执行详解
Jun 19 Javascript
vue-content-loader内容加载器的使用方法
Aug 05 Javascript
详解关于vue2.0工程发布上线操作步骤
Sep 27 Javascript
js实现继承的方法及优缺点总结
May 08 Javascript
JavaScript实现单点登录的示例
Sep 23 Javascript
js中判断Object、Array、Function等引用类型对象是否相等
Aug 29 #Javascript
xml转json的js代码
Aug 28 #Javascript
基于jquery创建的一个图片、视频缓冲的效果样式插件
Aug 28 #Javascript
javascript 判断中文字符长度的函数代码
Aug 27 #Javascript
Jquery 数据选择插件Pickerbox使用介绍
Aug 24 #Javascript
javascript数字格式化通用类 accounting.js使用
Aug 24 #Javascript
jquery动画4.升级版遮罩效果的图片走廊--带自动运行效果
Aug 24 #Javascript
You might like
PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解
2013/06/25 PHP
PHP生成制作验证码的简单实例
2016/06/12 PHP
PHP Ajax JavaScript Json获取天气信息实现代码
2016/08/17 PHP
使用PHP+MySql+Ajax+jQuery实现省市区三级联动功能示例
2017/09/15 PHP
thinkphp5 + ajax 使用formdata提交数据(包括文件上传) 后台返回json完整实例
2020/03/02 PHP
Jquery Ajax请求代码(2)
2011/01/07 Javascript
addEventListener和attachEvent二者绑定的执行函数中的this不相同
2012/12/09 Javascript
判断JS对象是否拥有某属性的方法推荐
2016/05/12 Javascript
Jquery为DIV添加click事件的简单实例
2016/06/02 Javascript
jQuery插件学习教程之SlidesJs轮播+Validation验证
2016/07/12 Javascript
AngularJs Modules详解及示例代码
2016/09/01 Javascript
Bootstrap基本插件学习笔记之Popover提示框(19)
2016/12/08 Javascript
Vue.js学习之计算属性
2017/01/22 Javascript
推荐三款不错的图片压缩上传插件(webuploader、localResizeIMG4、LUploader)
2017/04/21 Javascript
详解基于Node.js的微信JS-SDK后端接口实现代码
2017/07/15 Javascript
JavaScript实现获取两个排序数组的中位数算法示例
2019/02/26 Javascript
Vue可自定义tab组件用法实例
2019/10/24 Javascript
[57:55]EG vs Fnatic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
Python中的Matplotlib模块入门教程
2015/04/15 Python
python编程实现希尔排序
2017/04/13 Python
示例详解Python3 or Python2 两者之间的差异
2018/08/23 Python
11个Python3字典内置方法大全与示例汇总
2019/05/13 Python
python 3.74 运行import numpy as np 报错lib\site-packages\numpy\__init__.py
2019/10/06 Python
Python提取视频中图片的示例(按帧、按秒)
2020/10/22 Python
收集的22款给力的HTML5和CSS3帮助工具
2012/09/14 HTML / CSS
用JAVA SOCKET编程,读服务器几个字符,再写入本地显示
2012/11/25 面试题
毕业生的自我评价范文
2013/12/31 职场文书
高三体育教学反思
2014/01/29 职场文书
汽车队司机先进事迹材料
2014/02/01 职场文书
2014最新实习证明模板
2014/10/02 职场文书
婚礼新人答谢词
2015/01/04 职场文书
女性健康知识讲座通知
2015/04/23 职场文书
放飞理想主题班会
2015/08/14 职场文书
带你了解CSS基础知识,样式
2021/07/21 HTML / CSS
根德5570型九灯四波段立体声收音机是电子管收音机的楷模 ? 再论5570
2022/04/05 无线电
Java十分钟精通进阶适配器模式
2022/04/06 Java/Android