深入分析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 相关文章推荐
基于jquery的网页SELECT下拉框美化代码
Oct 28 Javascript
判定是否原生方法的JS代码
Nov 12 Javascript
jQuery实现自定义checkbox和radio样式
Jul 13 Javascript
canvas 画布在主流浏览器中的尺寸限制详细介绍
Dec 15 Javascript
基于JavaScript实现活动倒计时效果
Apr 20 Javascript
AngularJS动态绑定ng-options的ng-model实例代码
Jun 21 Javascript
jQuery中的类名选择器(.class)用法简单示例
May 14 jQuery
基于Vue 服务端Cookies删除的问题
Sep 21 Javascript
微信小程序实现slideUp、slideDown滑动效果及点击空白隐藏功能示例
Dec 11 Javascript
JavaScript Array对象使用方法解析
Sep 24 Javascript
jquery将信息遍历到界面上实例代码
Jan 21 jQuery
用JS创建一个录屏功能
Nov 11 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
php 强制下载文件实现代码
2013/10/28 PHP
PHP使用GIFEncoder类处理gif图片实例
2014/07/01 PHP
php获取一个变量的名字的方法
2014/09/05 PHP
php微信公众平台开发(三)订阅事件处理
2016/12/06 PHP
PHP实现将多个文件压缩成zip格式并下载到本地的方法示例
2018/05/23 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
学习js所必须要知道的一些
2007/03/07 Javascript
JQuery CSS样式控制 学习笔记
2009/07/23 Javascript
使用Post提交时须将空格转换成加号的解释
2013/01/14 Javascript
js动态添加表格数据使用insertRow和insertCell实现
2014/05/22 Javascript
input:checkbox多选框实现单选效果跟radio一样
2014/06/16 Javascript
一个检测表单数据的JavaScript实例
2014/10/31 Javascript
javascript实现表单提交后,提交按钮不可用的方法
2015/04/18 Javascript
javascript实现英文首字母大写
2015/04/23 Javascript
基于JavaScript实现带缩略图的轮播效果
2017/01/12 Javascript
微信小程序之MaterialDesign--input组件详解
2017/02/15 Javascript
微信小程序实现折叠面板
2018/01/31 Javascript
angular 实时监听input框value值的变化触发函数方法
2018/08/31 Javascript
基于vue开发微信小程序mpvue-docs跳转页面功能
2019/04/10 Javascript
vue项目中使用bpmn为节点添加颜色的方法
2020/04/30 Javascript
JQuery复选框全选效果如何实现
2020/05/08 jQuery
React中使用Vditor自定义图片详解
2020/12/25 Javascript
[01:53]3.19 DOTA2发布会 现场精彩Coser表演
2014/03/25 DOTA
python 添加用户设置密码并发邮件给root用户
2016/07/25 Python
tensorflow1.0学习之模型的保存与恢复(Saver)
2018/04/23 Python
python绘制圆柱体的方法
2018/07/02 Python
python接口调用已训练好的caffe模型测试分类方法
2019/08/26 Python
python中栈的原理及实现方法示例
2019/11/27 Python
python 连续不等式语法糖实例
2020/04/15 Python
html5 Canvas画图教程(11)—使用lineTo/arc/bezierCurveTo画椭圆形
2013/01/09 HTML / CSS
Bluebella美国官网:英国性感内衣品牌
2018/10/04 全球购物
生产助理岗位职责
2014/06/18 职场文书
聊聊Python中关于a=[[]]*3的反思
2021/06/02 Python
如何理解python接口自动化之logging日志模块
2021/06/15 Python
MySQL一些常用高级SQL语句
2021/07/03 MySQL
vue实现拖拽交换位置
2022/04/07 Vue.js