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

[亡魂溺海] 提交于 2019-12-03 14:26:56

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

1.1 同源策略

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

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)中的三部分都不相同,所以它们就是不同的域。 下面的图更好的解释了什么是同域:


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

1.3 为什么要有同源策略?

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

1.4 同源策略是限制谁的?

1) 很多人可能搞不清楚这个问题,同源策略限制的是浏览器或者其他提供类似浏览器服务的软件,而且这仅仅是个规范,所以浏览器是否遵守这个规范也不一定,所以就会有上面的IE浏览器判断是否同源的时候并没有考虑端口号的问题。 2) 同源策略限制的是js,而图片,css这些是不存在同源策略限制的。

1.5 什么是跨域?

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

2. 为什么会有跨域问题?

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

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

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

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/
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!