前端跨域问题

最近做一个项目,有遇到跨域问题,主要是涉及到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/

几种跨域方式的缺点:

  1. [document.domain]无法实现不同主域之间的通讯,并且一个页面上不止一个iframe,会产生安全性异常,拒绝访问
  2. [scrpt]标签方式对返回的数据格式要求比较严格,只能是json格式数据或者数组(如:{test:'hello'}或者\[2,1\])
  3. jsonp方式,服务器端程序运行比脚本造,跨域交互时基本上无法捕获钱孤单页面Dom元素的相关数据
  4. hash对值可以这样,比较复杂的不合适
  5. 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/';