跨域通信/跨域上传浅析
web项目跨域问题主要包括跨域通信和跨域上传,下面对这两方面分别做一个分析,具体项目中用哪个方案要看项目具体需求。
跨域通信
- jsonp
- hash
- server proxy
- window.name
- cors
- postmessage
- redirect
jsonp
原理:发起一个GET请求,回调函数带到请求参数中,把数据发送过去
坏处:服务器需要支持jsoncallback参数
好处:业界比较通用的方案,包括打点等操作都可以用类似技术
浏览器支持:chrome/firefox/safari/opera/ie6+
client:
//jquery $(function(){ $.ajax({ url: 'http://bbb.com:8888/crossdomain/jsonp.php', dataType: 'jsonp', jsonp : 'callback', //指定callback参数名 data: {name: 'ysj'} //数据 }).done(function(result){ alert(result) // {code:200} }) }) //实际请求:http://bbb.com:8888/crossdomain/jsonp.php?callback=jQuery111108173922670539469_1410615060808&name=ysj&_=1410615060809 //native js var temp = document.createElement('script'); temp.src = 'http://bbb.com:8888/crossdomain/jsonp.php' + '?callback=jsonpCallback&name=ysj'; //设置callback参数 document.body.appendChild(temp) temp.parentNode.removeChild(temp); function jsonpCallback(result) { alert(result) // {code:200} }
server:
$callback = $_GET['callback']; echo $callback . "({code:200})";
hash
原理:父页面把参数设置设置到子iframe的hash,子iframe监听hash变化,把响应的数据设置到父页面的hash
坏处:需服务器对应支持,目前hash比较常用于路由,用于通信不太合适
好处:无
浏览器支持:chrome/firefox/safari/opera/ie6+
代码略
server proxy:
原理:a域请求b域,在a域设置一个proxy服务器脚本页面,接受a的参数,在通过发起一个http请求拿b域的内容,再返回给a域
坏处:在a域服务器需要增加一个proxy页面
好处:比较通用的方案
浏览器支持:chrome/firefox/safari/opera/ie6+
代码略
window.name
原理:a域请求b域的页面,b加载完后设置响应数据到window.name,再重定向到a的一个页面,此时window.name并没有改变
坏处:b域需重定向到a域的一个处理页面
好处:无
浏览器支持:chrome/firefox/safari/opera/ie6+
代码略
cors(跨域资源共享):
原理:服务器设置可访问性Access-Control-Allow-Origin: *,浏览器ajax取数据
坏处:需要服务器特殊处理
好处:有比较好的未来
浏览器支持:chrome/firefox/safari/opera/ie10+
代码略
postmessage:
原理:利用html5的postmessage api跨域穿数据
坏处:兼容性
好处:有比较好的未来
浏览器支持:chrome/firefox/safari/opera/ie10+
代码在下面跨域上传中演示
redirect:
原理:a域iframe请求b域,把url带上,b域把response带上,返回到a域的handler页面
坏处:有安全隐患
好处:简单又浏览器支持良好的方案
浏览器支持:chrome/firefox/safari/opera/ie6+
代码在下面跨域上传中演示
跨域上传
- cors
- redirect
- postmessage
cors
原理:利用XMLHttpRequest2+FormData,服务器设置Access-Control-Allow-Origin: *,跨域资源访问这个特性,加上html5的FormData可以发送文件的特性。
坏处:需要服务器设置支持跨域访问 Access-Control-Allow-Origin: *
好处:又比较好的未来
浏览器支持:chrome/firefox/safari/opera/ie10+
client:
document.forms[0].onsubmit = function() { var xhr = new XMLHttpRequest(); var data = new FormData(document.forms[0]); xhr.open('POST' , this.action) xhr.onload = function(){ console.log(xhr.responseText) } xhr.send(data) return false }
server:
header("Access-Control-Allow-Origin: *"); echo "{code:200}";
redirect
原理:a域表单提交到b域,a的表单target指向a域的一个iframe,b域重定向到a域的结果页面,把数据带上。需要浏览器发送一个redirect参数,服务器检查到有这个参数,则重定向到结果页面,把内容带到参数上面
坏处:服务器和客户端特殊参数redirect处理
好处:支持所有浏览器
浏览器支持:chrome/firefox/safari/opera/ie6+
client:
<form method="post" action="http://bbb.bbb.com:8888/crossdomain/redirect.php" target="_iframe"> <input type="hidden" name="redirect" id="redirect"> <input type="file" name="Filedata"> <button type="submit">submit</button> </form> <iframe id="_iframe" name="_iframe"></iframe> document.forms[0].onsubmit = function() { document.getElementById('redirect').value = window.location.href.replace(/\/[^\/]*$/,'/result.html?%s'); //设置redirect参数值 var iframe = document.getElementById('_iframe') iframe.onload = function(){ console.log(iframe.contentDocument ? iframe.contentDocument.body.innerHTML : iframe.document.body.innerHTML) // {code:200} } }
redirect页面
document.body.innerText=document.body.textContent=decodeURIComponent(window.location.search.slice(1)); //把参数中的数据写入文档
server:
$redirect = isset($_REQUEST['redirect']) ? stripslashes($_REQUEST['redirect']) : null; $json = '{code:200}'; //有redirect参数,则重定向。需要注意的是服务器需要设置一个白名单,符合的才给予重定向,否则有安全问题 if ($redirect) { //页面重定向,把返回数据替换掉redirect url中的%s header('Location: '.sprintf($redirect, rawurlencode($json))); }
postmessage
原理:a域准备好参数通过postmessage发送到b域的postMessageAPI页面,这个页面把数据用XHR+FormData的方式提交a域请求的url,postMessageAPI再把返回的数据postmesage到a域的页面
坏处:b端服务器需准备一个postmessageAPI页面
好处:有比较好的未来
浏览器支持:chrome/firefox/safari/opera/ie10+
client:
<form method="post"> <input type="file" name="Filedata" id="file"> <button type="submit">submit</button> </form> <iframe src="http://bbb.com:8888/crossdomain/postMessage.html"></iframe> $('form').submit(function(){ var iframe = $('iframe')[0]; //向iframe的postmessate api发送消息 iframe.contentWindow.postMessage({ url : 'http://bbb.com:8888/crossdomain/postMessage.php', dataType:'json', data : { file : $('input[type=file]')[0].files[0] //把文件数据传过去 }, contentType:false, processData:false }, iframe.src) return false; }) //拿到postmessageAPI返回过来的消息 $(window).on('message', function(e){ var e = e.originalEvent; alert(e.data) // {code:200} })
postmesage.html/postmessagetAPI页面
//等待接受消息 $(window).on('message', function(e){ var e = e.originalEvent; var options = e.data; var truedata = options.data; var formdata = new FormData() for(var prop in truedata) { formdata.append(prop,truedata[prop]); } options.data = formdata; //向postmessage.php发送ajax请求,都在b域,不跨域 $.ajax(options).always(function(result){ //把响应数据再postmessage到请求的页面 e.source.postMessage(result.responseText, e.origin) }) })
server: postMessaget.php
echo "{code:200}";
跨子域通用方案
原理:a域aaa.aaa.com,b域bbb.aaa.com。客户端和服务器返回都设置document.domain = 'aaa.com'
坏处:有安全隐患
好处:比较方便
浏览器支持:chrome/firefox/safari/opera/ie7+
server:
//可以设置为客户端传来的domain参数,但需要白名单
<echo> "<script>document.domain='aaa.com'</script>";
<echo> "{code:200}";
此方案有个需要注意的地方:ie下面对长度小于5个字符的短域名不能设置domain,例如www.uc.cn