ajax跨域请求原理及解决方案分析

什么是跨域(Cross-site)?

想了解跨域,必须先了解一下“同源策略(same origin policy)”。

同源策略

它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。它限制了某个域下的文档或者js与另一个域中的资源交互的方式,它提供了一种安全机制,这种安全机制可以避免来自恶意网站的攻击。 同源策略要求浏览器当且仅当两个页面来自相同的域才允许其中一个网页上的js请求另一个网页的数据

什么是域(origin)?

域是由三部分组合而成:

  • URI Schema(协议类型),
  • host name(域名),
  • port number(端口号)

举个例子:


PS:IE浏览器里可能不太一样,它不会把端口号作为判断依据。

为什么要有同源策略?

提出同源策略的目的是出于安全性考虑,它能够阻止来自恶意网站的脚本通过其他网站的DOM获取其他网站的信息。可以避免CSRF和XSS攻击。

同源策略限制了啥?

  • 同源策略限制的是浏览器或者其他提供类似浏览器服务的软件,而且这仅仅是个规范;
  • 同源策略只是限制JavaScript,而图片,css这些是不存在同源策略限制的。

什么是跨域?

在A网站的页面上通过js请求B网站的数据,如果A和B两个网站不满足同源策略,那么就存在跨域问题。

为什么会有跨域问题?

由于在实际环境中,经常需要通过js获取一些数据,特别是ajax的流行,通过ajax加载某个网站的数据的场景就会经常遇到,而一旦有这样的需求,就可能会出现跨域的问题。

如何判断我是否遇到了跨域问题?

一般来讲,如果你的请求被同源策略限制,浏览器的开发工具都会给出错误提示,在Chrome浏览器的console中,可能会有类似下面的提示

如何解决跨域问题?

1、Jsonp方式

什么是JSONP?

JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

JSONP有什么用?

由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。

如何使用JSONP?

下边这一DEMO实际上是JSONP的简单表现形式,在客户端声明回调函数之后,客户端通过script标签向服务器跨域请求数据,然后服务端返回相应的数据并动态执行回调函数。

【html示例代码一】

<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />  
<script type="text/javascript">  
    function jsonpCallback(result) {  
        //alert(result);  
        for(var i in result) {  
            alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
        }  
    }  
    var JSONP=document.createElement("script");  
    JSONP.type="text/javascript";  
    JSONP.src="http://crossdomain.com/services.php?callback=jsonpCallback";  
    document.getElementsByTagName("head")[0].appendChild(JSONP);  
</script>  

【html示例代码二】

<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />  
<script type="text/javascript">  
    function jsonpCallback(result) {  
        alert(result.a);  
        alert(result.b);  
        alert(result.c);  
        for(var i in result) {  
            alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
        }  
    }  
</script>  
<script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script>  

【js示例代码一】

<script type="text/javascript" src="jquery.js"></script>  
<script type="text/javascript">  
    $.getJSON("http://crossdomain.com/services.php?callback=?",  
    function(result) {  
        for(var i in result) {  
            alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
        }  
    });  
</script>  

【js示例代码二】

<script type="text/javascript" src="jquery.js"></script>  
<script type="text/javascript">  
    $.ajax({  
        url:"http://crossdomain.com/services.php",  
        dataType:'jsonp',  
        data:'',  
        jsonp:'callback',  
        success:function(result) {  
            for(var i in result) {  
                alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
            }  
        },  
        timeout:3000  
    });  
</script>  

【js示例代码三】

<script type="text/javascript" src="jquery.js"></script>  
<script type="text/javascript">  
    $.get('http://crossdomain.com/services.php?callback=?', {name: encodeURIComponent('tester')}, function (json) { for(var i in json) alert(i+":"+json[i]); }, 'jsonp');  
</script>

Jsonp原理:

首先在客户端注册一个callback, 然后把callback的名字传给服务器。
此时,服务器先生成 json 数据。
然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.
最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

2、设置document.domain属性

如果两个页面或者frame可以将document.domain属性设置成相同的值,那么也可以绕过同源策略限制。 假设两个页面分别是static.demo.comserver.demo.com,两个页面加载之后都通过js将document.domain设置成demo.com,这样接下来的ajax请求就可以绕过同源策略限制了。
但是:如果两个页面存在端口,比如static.demo.com:8080 和 server.demo.com:8090,由于document.domain只能设置域名,所以就不起作用。
这种方法针对页面,如果服务端返回的是json,而不是一个页面,所以没法将自己的域名设置成demo.com,但是可以通过另外一种方式,即在服务端增加一个静态页面,页面中放如下js代码:

document.domain=demo.com

或者如下代码:

try{document.domain = window.location.hostname.split('.').reverse().slice(0,2).reverse().join('.');}catch(e){}

然后客户端页面加载的时候先去调用一下这个静态页面就好了。

3、 CORS(Cross-Origin Resource Sharing)

原理MDN上讲的更清楚一些,点击这里
其实简单来说就是服务端在响应头中添加一个Access-Control-Allow-Origin 头部,头部的值为客户端的域名,比如:http://static.demo.com

这样就可以了。但是需要注意的是:CROS分为两种,一种是简单请求,一种是复杂请求,简单请求按照上面的方式是可以的,如果是复杂请求,浏览器会进行两步,先发一个options请求,这个请求称之为“预请求”,预请求实际上是个OPTIONS请求,类似于一个探测作用,如果服务端返回的头部通过了预请求的内容,则浏览器才会发起第二个真实请求。

4、客户端请求通过Nginx转发

原理:客户端的所有请求都直接发到客户端所在域名下,但是在客户端服务器增加一台nginx服务器,作为反向代理,如果是后端的url,直接代理转发到服务端,这样就不存在前端的跨域问题了。

nginx配置如下

server {
    listen       80;
    server_name  static.demo.com;    #可配置多个主机头
    charset utf-8,gbk,gb2312,gb18030; #可以实现多种编码识别

    location / {
        root   /home/wy/www/static.demo.com/ROOT;  #网站文件路径

        autoindex on;
        autoindex_exact_size off;
        autoindex_localtime on;
        index  default.html;
    }
    #所有/server/开头的请求都会走这里
    location /server/ {
            proxy_pass http://server.demo.com:8080;  ##转发到server
            proxy_set_header    Host             $host;
            proxy_set_header    X-Real-IP        $remote_addr;
            proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
}

参考文章

https://my.oschina.net/jasonultimate/blog/550737

http://justcoding.iteye.com/blog/1366102/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容