跨域是什么?
假设a.com/get.html需要获取b.com/data.html中的数据,而这里a.com和b.com并不是同一台服务器,这就是跨域跨域会涉及到javascript的同源策略,简单来说就是为了保护网站的安全,不被外域(非同源)服务器的js修改本网站内容。
引用一个表格,看一下引起跨因的条件有哪些:
但是有时候我们确实需要这么做,那么我们有哪些方法呢?
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.htmlvar 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.htmldocument.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.htmlparent.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.htmliframe.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.htmlvar 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.htmlwindow.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 {// 否则,浏览器不支持corsxhr = 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。