深入分析Javascript跨域问题


Posted in Javascript onApril 17, 2015

跨域是什么?

假设a.com/get.html需要获取b.com/data.html中的数据,而这里a.com和b.com并不是同一台服务器,这就是跨域跨域会涉及到Javascript的同源策略,简单来说就是为了保护网站的安全,不被外域(非同源)服务器的js修改本网站内容。
引用一个表格,看一下引起跨因的条件有哪些:

深入分析Javascript跨域问题

但是有时候我们确实需要这么做,那么我们有哪些方法呢?

1、JsonP

提到跨域不能不先提及jsonp。jsonp其实是JavacScript Object Notation with Padding的简称,可以理解成填充了内容的json格式数据。
因为以上声明了callback并且调用外域b.com的data.js,而data.js中调用:
callback({msg:"tqtan"});
这样当调用引用外域的js就会调用本地的callback()从而实现数据传输。
上面是只是简单的跨域,我们来看jQuery的真正的运用。

jQuery中的ajax可拉取外域的数据,通过两种方法:

1、$.getJSON()

这种方法简单粗暴,请求外域Json。

$.getJSON("http://b.com/dataServlet?callback=?",function(data){

console.log(data.msg);

});

假设上述请求访问b.com下的servlet页面,传的参数为callback=?,jQuery会自动生成字符串填补占位符?,例如callback=jQuery17207481773362960666_1332575486681。这就声明了与服务器的唯一标示,服务器只需要返回带有这个callback值的json格式数据即可,例如:

//dataServlet.java

String callback = req.getParameter("callback");

PrintWriter out = resp.getWriter();

out.print(callback+"('msg','tqtan')");

这样就能成功获取非同源服务器的数据了。

2、$.ajax()

实现原理和上面一样,只是可以自定义更多链接。

$.ajax({
url:'http://b.com/dataServlet?words=hi',
dataType:'jsonp',
jsonp : 'jsoncallback',
jsoncallback : 'tqtan',
success:function(data){
console.log(data.msg);
},
error: function (e) {
console.log(e);
}
});

可以自定义callback的名字,这里改为'tqtan',同时这里可以传值words=hi。
注意了,JsonP格式只能是以GET形式请求服务器。

2、document.domain

这种方法只适用于主域相同,而子域不同的跨域。
也就是get.a.com和data.a.com的跨域问题,解决方法很简单:
若get.a.com/get.html需要获取data.a.com/data.html的数据,首先在get.html插入一个iframe,src指向data.a.com/data.html,然后在data.html写上document.domain='a.com';即可操纵data.html内的内容。

//get.html
var iframe = document.creatElement("iframe");
iframe.src="http://data.a.com/data.html";
iframe.style.display="none";
document.body.appendChild(iframe);
document.domain = 'a.com';
iframe.onload = function(){
var otherDocument = iframe.contentDocument || iframe.contentWindow.document;
//otherDocument就是另一个页面的document
//do whatever you want..
};
//data.html
document.domain = 'a.com';

3、url hash

你也可以通过url的hash来实现跨域。hash就是url#后面的内容,例如http://targetkiller.net/index.html#data,这里#data就是hash。怎么用这个实现跨域呢?

还是那个例子,a.com/get.html需要获取b.com/data.html,首先在get.html建立一个iframe,src还是指向data.html,后面带上hash值实现传参。另一端data.html根据获取的hash作出响应,自身也创建一个iframe,src指向a.com/proxy.html,并把响应数据添加到hash。之后,a.com/proxy.html只需要修改在同一a.com父域的get.html的hash即可。最后,怎样获取数据呢?只需要在get.html写一个定时器setInterval,定期监听有无新的hash即可。

看到这里,你可能感到开始乱了,几个问题:

1.proxy.html的作用?
由于get.html和data.html不在一个域上,所以不能修改location.hash值,于是运用proxy.html,先跳到找个代理页面,然后通过parent.location.hash,也就是修改父亲,让儿子(get.html)也得到响应。
a.com/get.html

var iframe = document.createElement('iframe');
iframe.src = 'http://a.com/get.html#data';
iframe.style.display = 'none';
document.body.appendChild(iframe);
//周期检测hash更新
function getHash() {
var data = location.hash ? location.hash.substring(1) : '';
console.log(data);
}
var hashInt = setInterval(function(){getHash()}, 1000);
a.com/proxy.html
parent.location.hash = self.location.hash.substring(1);
b.com/data.html
//模拟一个简单的参数处理操作
if(location.hash){
var data = location.hash;
doSth(data);
}
function doSth(data){
console.log("from a.com:"+data);
var msg = "hello i am b.com";
var iframe = document.createElement('iframe');
iframe.src = "http://a.com/proxy.html#"+msg;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}

4、window.name

这种方法比较巧妙,引用圆心的解释,name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
具体例子依旧如上,同时也是需要一个代理页面。
a.com/get.html请求b.com/data.html,首先get.html创建一个iframe,src指向data.html,然后监听iframe的onload事件。与此同时,在data.html设置window.name = data;把window.name赋值。然后onload事件后马上把iframe的跳到本地a.com/proxy.html。因此window.name就共享到了src为proxy.html的找个iframe中,接下来,就是同源间获取值的事了。
a.com/get.html

var state = 0,
iframe = document.createElement('iframe'),
iframe.src = 'http://b.com/data.html";
iframe.style.display = 'none';
loadfn = function() {
if (state === 1) {
var data = iframe.contentWindow.name;
console.log(data);
} else if (state === 0) {
state = 1;
//跳到proxy.html
iframe.contentWindow.location = "http://a.com/proxy.html";
}
};
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
a.com/proxy.html
// proxy.html的操作主要是删除get.html的iframe,避免安全问题发生
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
b.com/data.html
var data = "hello,tqtan";
window.name = data;

5、 postMessage()

html5的新方法postMessage()优雅地解决了跨域,也十分容易理解。
发送方调用postMessage()内容,接受方监听onmessage接受内容即可。
假设发送方为a.com/send.html,接受方为b.com/receive.html。
a.com/send.html

var iframe = document.createElement("iframe");
iframe.src = "http://b.com/receive.html";
document.body.appendChild(iframe);
iframe.contentWindow.postMessage("hello","http://b.com");
b.com/receive.html
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://a.com') {
console.log(event.data);
console.log(event.source);//发送源的window值
}
}, false);

6、CORS(后台实现)

以上5点都是前端实现的跨域,但是后台参与会让跨域更容易解决,也就是用CORS。
CORS是Cross-Origin Resource Sharing的简称,也就是跨域资源共享。它有多牛逼?之前说JsonP只能get请求,但CORS则可以接受所有类型的http请求,然而CORS只有现代浏览器才支持。
怎样使用?前端只需要发普通ajax请求,注意检测CORS的支持度。引用自蒋宇捷。

function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// 此时即支持CORS的情况
// 检查XMLHttpRequest对象是否有“withCredentials”属性
// “withCredentials”仅存在于XMLHTTPRequest2对象里
xhr.open(method, url, true);
}
else if (typeof!= "undefined") {
// 否则检查是否支持XDomainRequest,IE8和IE9支持
// XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// 否则,浏览器不支持CORS
xhr = null;
}
return xhr;
}
var xhr = createCORSRequest('GET', url);
if (!xhr) {
throw new Error('CORS not supported');
}

与此同时,服务器端只需要设置Access-Control-Allow-Origin头即可。

java中你只需要设置

response.setHeader("Access-Control-Allow-Origin", "*");

为了安全,也可以将*改为特定域名,例如a.com。

Javascript 相关文章推荐
SWFObject 2.1以上版本语法介绍
Jul 10 Javascript
JavaScript-RegExp对象只能使用一次问题解决方法
Jun 23 Javascript
JavaScript常用基础知识强化学习
Dec 09 Javascript
JS动态计算移动端rem的解决方案
Oct 14 Javascript
javascript基本数据类型及类型检测常用方法小结
Dec 14 Javascript
获取IE浏览器Cookie信息的方法
Jan 23 Javascript
详解AngularJS1.6版本中ui-router路由中/#!/的解决方法
May 22 Javascript
js数组实现权重概率分配
Sep 12 Javascript
AngularJS模态框模板ngDialog的使用详解
May 11 Javascript
vue中的自定义分页插件组件的示例
Aug 18 Javascript
ES6 Promise对象的含义和基本用法分析
Jun 14 Javascript
微信小程序3种位置API的使用方法详解
Aug 05 Javascript
JavaScript实现MIPS乘法模拟的方法
Apr 17 #Javascript
JavaScript中split() 使用方法汇总
Apr 17 #Javascript
javascript模拟命名空间
Apr 17 #Javascript
JavaScript实现为指定对象添加多个事件处理程序的方法
Apr 17 #Javascript
完美兼容多浏览器的js判断图片路径代码汇总
Apr 17 #Javascript
JavaScript控制网页平滑滚动到指定元素位置的方法
Apr 17 #Javascript
原生js实现移动开发轮播图、相册滑动特效
Apr 17 #Javascript
You might like
PHP4实际应用经验篇(3)
2006/10/09 PHP
php+redis实现商城秒杀功能
2020/11/19 PHP
PHP的mysqli_rollback()函数讲解
2019/01/23 PHP
PHP实现创建一个RPC服务操作示例
2020/02/23 PHP
return false,对阻止事件默认动作的一些测试代码
2010/11/17 Javascript
js实现拉伸拖动iframe的具体代码
2013/08/03 Javascript
一个JS函数搞定网页标题(title)闪动效果
2014/05/13 Javascript
JS实现具备延时功能的滑动门菜单效果
2015/09/17 Javascript
详解JQuery基础动画操作
2019/04/12 jQuery
解决layui的form里的元素进行动态生成,验证失效的问题
2019/09/14 Javascript
[38:32]完美世界DOTA2联赛循环赛 Forest vs DM 第二场 11.06
2020/11/06 DOTA
python处理圆角图片、圆形图片的例子
2014/04/25 Python
详解Python中的文本处理
2015/04/11 Python
Python字符串匹配算法KMP实例
2015/07/18 Python
关于python pyqt5安装失败问题的解决方法
2017/08/08 Python
Python基于正则表达式实现检查文件内容的方法【文件检索】
2017/08/30 Python
Python 文件操作的详解及实例
2017/09/18 Python
Python 获取主机ip与hostname的方法
2018/12/17 Python
Python小游戏之300行代码实现俄罗斯方块
2019/01/04 Python
Python实现RGB与HSI颜色空间的互换方式
2019/11/27 Python
Python实现中值滤波去噪方式
2019/12/18 Python
基于Python数据结构之递归与回溯搜索
2020/02/26 Python
python GUI库图形界面开发之PyQt5下拉列表框控件QComboBox详细使用方法与实例
2020/02/27 Python
PyCharm设置Ipython交互环境和宏快捷键进行数据分析图文详解
2020/04/23 Python
Python的控制结构之For、While、If循环问题
2020/06/30 Python
Python分类测试代码实例汇总
2020/07/23 Python
python subprocess pipe 实时输出日志的操作
2020/12/05 Python
详解java调用python的几种用法(看这篇就够了)
2020/12/10 Python
真正了解CSS3背景下的@font face规则
2017/05/04 HTML / CSS
Free People中国官网:波西米亚风格女装服饰
2016/08/30 全球购物
linux面试题参考答案(5)
2016/11/05 面试题
出纳岗位职责范本
2015/03/31 职场文书
2015年房产销售工作总结范文
2015/05/22 职场文书
2016七夕情人节感言
2015/12/09 职场文书
Mysql 用户权限管理实现
2021/05/25 MySQL
Java基础之this关键字的使用
2021/06/30 Java/Android