最近做一个项目,有遇到跨域问题,主要是涉及到flash跨域和前端请求跨域,这里跟大家分享下处理这个两个跨域的方法
flash跨域策略:
flash跨域比较简单,先介绍flash跨域:
(1)flash在跨域时唯一的限制策略就是crossdomain.xml文件,该文件限制了flash是否可以跨域读写数据以及允许从什么地方跨域读写数据。
(2)位于www.a.com域中的SWF文件要访问www.b.com的文件时,SWF首先会检查www.b.com服务器目录下是否有crossdomain.xml文件, 如果没有,则访问不成功;若crossdomain.xml文件存在,且里边设置了允许www.a.com域访问,那么通信正常。 所以要使Flash可以跨域传输数据,其关键就是crossdomain.xml。
(3)crossdomain.xml文件的内容:
<!DOCTYPE cross-domain-policy SYSTEM “/xml/dtds/cross-domain-policy.dtd”>
(4)把crossdomain.xml放在服务端的服务器上便可以进行flash跨域访问了。 详细的介绍可以参考http://www.2cto.com/Article/201108/100008.html
前端请求跨域:
由于浏览器同源策略的限制,不同域之间属性和操作时无法直接交互的。所以才会出现跨域问题。跨域问题是针对浏览器端来说的, 服务器端是不存在跨域安全限制的。处理跨域的两类解决方案:第一种就是服务器端做中转处理方式,一类是js处理浏览器端的真正 跨域访问
一、 同主域下面,不同子域之间的跨域;
同主域,不同子域跨域的情况,比如:wenjuanapi.apps.91.com,static.apps.91.com,他们的主域都是apps.91.com,所以 只要设置两个站点互相通信的页面的document.domain就可以解决了。比较常用
子页访问父页,可以使用window.parent的全局属性
注意的是:设置document.domain这种方式只能用在父、子页面之中,即只有在用iframe进行数据访问时才有用;不能 在不同主域下面使用。下面一个例子讲解iframe的原理和如何实现跨域方式
二、 不同主域跨域;
不同主域的情况比如:www.a.com和www.b.com
(1)可以通过sricpt标签加载,script标签的src属性和img标签的src属性所指向的资源一样,都是一个静态资源, 浏览器会在适当的时候自动去加载这些资源,不会出现跨域问题。比如:www.aa.com下面的一个a.jsp页面,www.bb.com/b.jsp a.jsp的页面代码如下:
<script type="text/javascript">
function myTest(data){
console.log(data);
}
</script>
<script type="text/javascript" src="http://www.bb.com/survey/getlist?jsoncallback=myTest">
</script>
b.jsp的页面代码如下:
$(param.jsoncallback)({'name':'zhangsan','QQ':'2564511'});
b.jsp页面通过$(param.jsoncallback)得到浏览器要回调的js函数名:myTest。
实际上客户端接受到服务端返回的数据如下:
myTest({'name':'zhangsan','QQ':'2564511'});
缺点是:对返回的数据格式要求比较严格,只能是json格式数据或者数组(如:{test:’hello’}或者[2,1])
(2)服务器端做代理,在www.aa.com下面建一个代理,直接访问www.aa.com/index.php,这个服务端页面去处理请求发送给www.bb.com下面 的接口来获取数据,再通过代理把数据返回给客户端。下面也会有个例子进行重点说明比较常用
(3)目前jquery的$.ajax()只支持get方式的跨域,采用的jsonp方式完成的,设置dataType:jsonp,设置jsonpCallback。 原理就是上面(2)的方式,动态添加了<script>标签的src属性实现跨域访问。服务器端代码总是优先于javascript代码 <ol> <li> 首先在客户端注册一个callback(如’jsoncallback’),然后把callback的名字(如:jsonp123)传给服务器</li> <li>然后用javascript语法生成一个函数名字就是传递上来的参数</li> <li>最后即那个返回的数据,放到生成的函数里面,返回给客户端,客户端浏览器,解析script标签,并执行脚本</li> <li>jquery封装的就是传到success方法里面动态执行函数</li> </ol> (4)hash可以实现跨域传值实现跨域通讯。比较复杂的不适合
(5)window.navigator这个对象是在所有的iframe中均可以访问的对象,不支持IE外的浏览器下的跨域
(6)window.name,具体的可以参考http://www.planabc.net/2008/09/01/window_name_transport/
几种跨域方式的缺点:
- [document.domain]无法实现不同主域之间的通讯,并且一个页面上不止一个iframe,会产生安全性异常,拒绝访问
- [scrpt]标签方式对返回的数据格式要求比较严格,只能是json格式数据或者数组(如:{test:'hello'}或者\[2,1\])
- jsonp方式,服务器端程序运行比脚本造,跨域交互时基本上无法捕获钱孤单页面Dom元素的相关数据
- hash对值可以这样,比较复杂的不合适
- window.navigate不支持IE外的浏览器下的跨域,严格的dtd申明后,该方法失效
例子: 同主域,不同子域下的例子:
(1)使用iframe跨域,在前端有请求服务端的页面,都加入一个iframe,iframe的地址是服务端上的一个代理页面 设置这个代理页面的域和前端请求页面的域一样,就可以了。。
(2)因为服务端的这个代理页面,放在服务端的服务器上,和服务器是同源的,可以直接通信。。而前端页面嵌入了这个代理页面,前端页面 又和这个代理页面是同个域的,也可以正常通讯。所以这样子就解决了通信问题
(3)下面是代码: 放在服务端上的代理页面proxy.html的代码://wenjuanapi.apps.91.com
<!DOCTYPE html>
<html>
<head>
<title>proxy</title>
<script src="http://static.apps.91.com/wenjuan/public/js/seajs/sea.js"></script>
<script>
document.domain = 'apps.91.com';
seajs.use('http://static.apps.91.com/wenjuan/public/js/jquery/jquery.js', function($) {
if(!window.parent.crossDomain) {
window.parent.crossDomain = {};
}
window.parent.crossDomain.ajax = $.ajax;
});
</script>
</head>
<body>
</body>
</html>
前端代码://static.apps.91.com
define(function(require, exports, module) {
var $ = require('$');
document.domain = 'apps.91.com';
var proxyUrl = 'http://wenjuanapi.apps.91.com/proxy.html';
var id = 'proxyIframe' + (+new Date());
var ready = false;
var cache = [];
var Ajax = function() {
if(ready) {
crossDomain.ajax.apply(this, arguments);
} else {
cache.push(arguments);
}
};
$(function() {
//ie的兼容性
var iframe = $('#' + id);
var fmState=function() {
var state = null;
try {
state = iframe.document.readyState;
} catch (e) {
state = null;
}
if(state==='complete'||!state)
{
var args;
ready = true;
while (args = cache.shift()) {
Ajax.apply(this, args);
}
}
}
if (!(iframe && iframe[0])) {
iframe = $('<iframe src="' + proxyUrl + '" name="' + id + '" id="' + id + '" frameborder="0" width="0" height="0"></iframe>');
$(document.body).append(iframe);
if(document.all){
//如果是ie浏览器,加载iframe
setTimeout(fmState,400);
}else{
iframe.load(function () {
var args;
ready = true;
while (args = cache.shift()) {
Ajax.apply(this, args);
}
});
}
}
});
module.exports = Ajax;
}); ---------------------------------------------------------------------------------
(4)后面代码做了修改,可以捕获服务端回来的状态,贴出来学习下:
var Ajax = function () {
if (ready) {
var args = arguments[0];
var config;
if (typeof args === 'string') {
config = arguments[1];
config.url = args;
} else {
config = args;
}
config.statusCode = {
301: function (data) {
var result = data.responseJSON;
var uid = result.uid;//新的用户id值
var unitid = result.unitid;//新的单位值
var role = result.role;//新的角色值
var pop_text = "身份已切换";
if (uid == 0)//需要重新选身份
{
window.location.href = appUrl;
}
new Dialog({
title: "身份切换确认",
width: 450,
height: 190,
content: poptpl.render({prompt_text: pop_text, uid: uid, unitid: unitid, role: role})
}).after('hide',function () {
this.destroy();
}).show();
$("a.dialog-close").hide();
},
600: function (data) {
window.location.href = appUrl;
}
};
crossDomain.ajax.call(this, config);
} else {
cache.push(arguments);
}
};
例子:不同主域的例子://sjq.91.com,服务器端域名sjqapi.192.168.94.26.xip.io
在sjq.91.com的根目录下放一个proxy.php页面,用来处理浏览器的请求.
客户端请求的地址类似这样:http://sjq.91.com/backend/proxy.php?method=scene%2Fbk_search%3Fapprove_stat%3D0%26size%3D40%26order%3Dasc%26timestamp%3D0&access_token=2d26068aef61d08db47beca16a21d5df54286993
proxy.php页面去处理sjqapi.192.168.94.26.xip.io从这个域下获取数据,返回给浏览器解析。
private $baseUrl = 'http://sjqapi.192.168.94.26.xip.io/';