跨域通信/跨域上传浅析

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

 

相关代码:https://github.com/aoto/cross-domain

posted @ 2014-10-15 21:42  aotoYu  阅读(470)  评论(0编辑  收藏  举报