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 相关文章推荐
通过MSXML2自动获取QQ个人头像及在线情况(给初学者)
Jan 22 Javascript
10个基于jQuery或JavaScript的WYSIWYG 编辑器整理
May 06 Javascript
解决extjs grid 不随窗口大小自适应的改变问题
Jan 26 Javascript
extjs 如何给column 加上提示
Jul 29 Javascript
JQuery中绑定事件(bind())和移除事件(unbind())
Feb 27 Javascript
jQuery背景插件backstretch使用指南
Apr 21 Javascript
浅谈JS正则表达式的RegExp对象和括号的使用
Jul 28 Javascript
Textarea输入字数限制实例(兼容iOS&amp;安卓)
Jul 06 Javascript
js自定义trim函数实现删除两端空格功能
Feb 09 Javascript
微信小程序首页的分类功能和搜索功能的实现思路及代码详解
Sep 11 Javascript
如何使用JavaScript实现无缝滚动自动播放轮播图效果
Aug 20 Javascript
浅谈node.js中间件有哪些类型
Apr 29 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编实现程动态图像的创建代码
2008/09/28 PHP
逆序二维数组插入一元素的php代码
2012/06/08 PHP
基于wordpress主题制作的具体实现步骤
2013/05/10 PHP
php防止sql注入代码实例
2013/12/18 PHP
php开启与关闭错误提示适用于没有修改php.ini的权限
2014/10/16 PHP
PHP微信开发之文本自动回复
2016/06/23 PHP
thinkPHP5.0框架事务处理操作简单示例
2018/09/07 PHP
CSS+Table图文混排中实现文本自适应图片宽度(超简单+跨所有浏览器)
2009/02/14 Javascript
jQuery:节点(插入,复制,替换,删除)操作
2013/03/04 Javascript
JavaScript包装对象使用介绍
2013/08/29 Javascript
js简单的点击返回顶部效果实现方法
2015/04/10 Javascript
JQuery菜单效果的两个实例讲解(3)
2015/09/17 Javascript
node-http-proxy修改响应结果实例代码
2016/06/06 Javascript
重新认识vue之事件阻止冒泡的实现
2018/08/02 Javascript
详解Webstorm 下的Angular2.0开发之路(图文)
2018/12/06 Javascript
javascript实现计算器功能
2020/03/30 Javascript
Vue-cli 移动端布局和动画使用详解
2020/08/10 Javascript
JavaScript实现页面高亮操作提示和蒙板
2021/01/04 Javascript
python类定义的讲解
2013/11/01 Python
Python中的引用和拷贝浅析
2014/11/22 Python
Python2和Python3中urllib库中urlencode的使用注意事项
2018/11/26 Python
Python Tensor FLow简单使用方法实例详解
2020/01/14 Python
Python接口自动化判断元素原理解析
2020/02/24 Python
numpy实现RNN原理实现
2021/03/02 Python
HTML5实现自带进度条和滑块滑杆效果
2018/04/17 HTML / CSS
英国自行车商店:AW Cycles
2021/02/24 全球购物
英语专业毕业生自我鉴定
2013/11/09 职场文书
职业生涯规划设计步骤
2014/01/12 职场文书
小学开学寄语
2014/01/19 职场文书
小学生成长感言
2014/01/30 职场文书
给校长的建议书300字
2014/05/16 职场文书
试用期转正员工自我评价
2014/09/18 职场文书
慈善募捐倡议书
2015/04/27 职场文书
企业投资意向书
2015/05/09 职场文书
2016年大学校运会广播稿件
2015/12/21 职场文书
Html5大屏数据可视化开发的实现
2021/06/11 HTML / CSS