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

##1. 什么是跨域(Cross-site)?
想了解跨域,必须先了解一下“同源策略(same origin policy)”。javascript

1.1 同源策略

它限制了某个域下的文档或者js与另外一个域中的资源交互的方式,它提供了一种安全机制,这种安全机制能够避免来自恶意网站的攻击。 同源策略要求浏览器容许来自某个网页上的js请求来自另外一个网页的数据,当且仅当两个页面来自相同的域。css

1.2 什么是域(origin)?

域是由三部分组合而成:URI Schema(协议类型),host name(域名),port number(端口号)
举个例子:
1) http://www.domain.com 这个页面,URI Schema是http,host name是www.domain.com,port number是默认的80
2) https://www.xxx.com:8080/xxx/yyy URI Schema是https,hostname是www.xxx.com,port number是8080
因为1)和2)中的三部分都不相同,因此它们就是不一样的域。 下面的图更好的解释了什么是同域:
QQ图片20151224163925.png-15.9kB
PS:IE浏览器里可能不太同样,它不会把端口号做为判断依据。html

1.3 为何要有同源策略?

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

1.4 同源策略是限制谁的?

1) 不少人可能搞不清楚这个问题,同源策略限制的是浏览器或者其余提供相似浏览器服务的软件,并且这仅仅是个规范,因此浏览器是否遵照这个规范也不必定,因此就会有上面的IE浏览器判断是否同源的时候并无考虑端口号的问题。 2) 同源策略限制的是js,而图片,css这些是不存在同源策略限制的。java

1.5 什么是跨域?

在某个网站的页面上经过js请求另一个网站的数据,若是两个网站不知足同源策略,那么就存在跨域问题。nginx

2. 为何会有跨域问题?

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

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

通常来说,若是你的请求被同源策略限制,浏览器的开发工具都会给出错误提示,在Chrome浏览器的console中,可能会有相似下面的提示:
QQ截图20151224171533.png-110.1kBjson

4.如何解决跨域问题?

通常的思路是:经过一些妥协调整,绕过同源策略的限制。下面是我最近了解的一些方法。
为方便讲解,这里先举一个例子:
客户端采用H5开发,全部的数据都经过ajax请求从服务端获取。
客户端的页面都存放在静态文件服务器中,域名是http://static.demo.com
服务端提供接口供客户端调用,接口的参数和返回值都是JSON格式,服务端的域名是:http://server.demo.com。
若是不考虑跨域的问题,客户端与服务端的交互方式以下:
1.客户端post请求服务端,参数:{"key":"value"}
2.服务端返回结果:{"code":1,"data":"success"}后端

4.1 Jsonp方式

原理: 经过在页面中新增一个<script>标签,标签的src指向的是另一个域的可以提供数据的url,同时将一个本地的callback方法传给服务端,服务端返回的时候将会自动执行callback方法。
实现举例:
1)服务端修改返回的数据类型为js,同时在请求参数中增长一个callback字段,这个字段用于客户端传递要执行的js方法名称。 2)客户端传递的参数中增长callback,同时将普通的ajax方法改为在页面中新增一个<script>节点的方式。 具体实现:跨域

1.经过js在页面中append以下标签

<script type="application/javascript"
        src="http://server.demo.com/Users/1234?callback=parseResponse"></script>

增长该标签以后,浏览器就会当即去请求这个url,因为<script src="">方式的是不受同源策略限制的,因此能够避免跨域限制。

2.服务端收到callback参数以后,将它拼接在返回的数据中,返回的数据以下:

parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7});

3.这样返回以后,就调用页面上的parseResponse js方法,就达到了数据处理的目的。
4.最后将刚刚新增长到页面中的<script>元素删掉。

4.2 设置document.domain属性

若是两个页面或者frame能够将document.domain属性设置成相同的值,那么也能够绕过同源策略限制。 假设两个页面分别是static.demo.com和server.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){}

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

4.3 CORS(Cross-Origin Resource Sharing)

原理MDN上讲的更清楚一些,点击这里。 其实简单来讲就是服务端在响应头中添加一个Access-Control-Allow-Origin头部,头部的值为客户端的域名,好比:http://static.demo.com,这样就能够了。
可是须要注意的是:CROS分为两种,一种是简单请求,一种是复杂请求,简单请求按照上面的方式是能够的,若是是复杂请求,浏览器会进行两步,先发一个options请求,这个请求称之为“预请求”,预请求其实是个OPTIONS请求,相似于一个探测做用,若是服务端返回的头部经过了预请求的内容,则浏览器才会发起第二个真实请求。这个后续我会有详细文章介绍。

4.4 客户端请求经过Nginx转发

原理:客户端的全部请求都直接发到客户端所在域名下,可是在客户端服务器增长一台nginx服务器,做为代理,若是是后端的url,直接代理转发到服务端,这样就不存在前端的跨域问题了。
举例:

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;
        }
}

4.5 其余方式

1)WebSocket 2)Cross-document messaging

[参考资料]

  1. https://en.wikipedia.org/wiki/Same-origin_policy
  2. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
  3. https://en.wikipedia.org/wiki/Cross-site_request_forgery
  4. https://en.wikipedia.org/wiki/Cross-site_scripting
  5. https://en.wikipedia.org/wiki/JSONP
  6. http://www.w3.org/TR/cors/
相关文章
相关标签/搜索