跨域系列一:从跨域到CORS

一.介绍
  跨域,可能很多前端开发者都会遇到过,也可能知道有jsonp,iframe之类的跨域方法。不过要说这些方法之前,先得来说说什么叫跨域,为什么要跨域。
  所谓跨域,顾名思义,跨到了另外的域,域不仅仅指的是不同的域名网站,可能同一个域名不同的端口号也算不同的域。浏览器是有规则的,只要协议、域名、端口有任何一个不同,都被当作是不同的域。协议指的是http,或者https等。
  下面给出一个列表,指出不同域的情况:

  这个叫浏览器的同源策略(same-origin policy),为什么要这样规定呢,原因之一就是为了安全。

  假如你在A网站,用一段js脚本就能访问B网站,B网站凭什么让你访问,为什么浏览器能让你随便访问别的网站呢,说不定B网站存在着各种危险,比如盗取你的密码,跨域攻击(CSRF)等,一切都不太合理。

  但也是有例外的情况,有些情况是要访问到别的网站的,比如加载一张图片,这张图片可能在别的网站,还有加载一个js文件,也是有可能在别的网站的,还有嵌入一个frame元素,也可以访问到别的网站的内容。

  所以跨域正是利用了上面几点,比如jsonp就是利用的加载js文件的功能,比如在<script src="..."></script>中的src指定为目标网站的js,同理,还有跨域攻击也可以利用<image src="..." />的功能。还有iframe更能直接加载别的网站的内容到自己的网站里来。

  这前面几种可以说是技巧,说句不好听的就是浏览器的漏洞,浏览器并未从正面上支持跨域,而且上面的几种跨域方法也是有各种局限,比如jsonp的方法,只能用GET方法,iframe方法是能直接加载内容到网站上,但是本网站和iframe的数据交互也是一个头疼的地方。

  毕竟从iframe加载内容到本站后,是存在着数据交互的,可以用document.domain,window.name,不过这些方法都能用,且有效,不过总不尽完美,存在着各种各样的局限。

  然而HTML5引进了一个叫window.postMessage方法来跨域传送数据,这个倒是不错,不过也是利用了iframe。

  除此之外,还有flash,服务器代理等跨域方法,但是本章要介绍的是浏览器或服务器的跨域方法,它的名字叫CORS。

 

二.CORS
  CORS全称是Cross-Origin Resource Sharing,跨域资源共享,这是浏览器的标准,也算是协议,基本上现代浏览器都支持,除了奇葩浏览器,例如IE8、IE9,只支持部分特性。

  使用它,需要服务器端和客户端两方面的准备,服务器端我们选择nginx作为测试,客户端只是js罢了。

  nginx服务监听在localhost的8080端口,而现在有一个网站运行localhost的3000端口,需要跨域到nginx那台服务器。

  现在开始测试之旅,要在浏览器模拟跨域请求,只需三行js代码。

var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://10.107.98.46:8088/AngularTodoMVC/#/", true);
xhttp.send();

  我使用的浏览器是chrome,打开它的开发者工具,在console里运行上面的代码,效果如下:

  上面的报错已经提示得很明显了,主要是下面这句话:

Failed to load http://10.107.98.46:8088/AngularTodoMVC/#/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://10.107.98.46:8089' is therefore not allowed access.

  错误中说Access-Control-Allow-Origin这个头信息不存在于被请求的服务器中,来源域http://10.107.98.46:8089是不允许访问该服务器的。

  XMLHttpRequest这个可能很多开发者都明白,那是ajax请求利用的对象,利用它能发起ajax请求,但它的功能不仅仅是发起ajax请求,还能用于跨域,还有设置时限,FormData对象管理表单数据,文件上传等功能,具体可以自行搜索相关的资料,在这里,它能发起跨域请求就可以了。
  我们来看看这个请求相关的信息。

  最重要的是Origin:http://10.107.98.46:8089这一行,它标明的是来源的域,这是请求头信息,会传给服务器。而服务器还是正常响应200状态的。也就是说,服务器端该怎么应答就怎么应答,只是被浏览器给阻止了。

  浏览器是如何阻止的呢。主要是看响应头部的信息。

  它没有发现有发现Origin:http://10.107.98.46:8089这个来源域名是被服务器所允许的,我们在服务器端设置一下,让Origin:http://10.107.98.46:8089这个来源域被允许访问。即在/AngularTodoMVC下新增add_header 'Access-Control-Allow-Origin' '*';配置,如下所示:

server {
    listen       8088;
    server_name  localhost;

            location / {
                 #add_header 'Access-Control-Allow-Origin' '*';
                 #root html;
            }

            location /AngularTodoMVC {
                    add_header 'Access-Control-Allow-Origin' '*';
                    proxy_pass   http://10.107.98.46:21240/AngularTodoMVC;
                    proxy_redirect off;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header Host $http_host;
            }

            error_page  404              /404.html;
            location = /404.html {
                            root   html;
            }

            error_page  500 502 503 504  /50x.html;
            location = /50x.html {
                            root   html;
            }
}

  add_header 'Access-Control-Allow-Origin' '*'表示允许任何来源域访问nginx这台服务器。

  用sudo nginx -s reload重新加载nginx服务器配置。
  并清除浏览器缓存,再来看下效果。

  果然成功了,不再提示错误。来看下响应的信息。

  响应信息中多了这一行Access-Control-Allow-Origin:*。

  原来,浏览器在用XMLHttpRequest发起跨域请求的时候,它在请求头带了Origin这个项,而服务器,在响应头信息中是有响应Access-Control-Allow-Origin这项的,两者比较一下,如果匹配,则请求成功,不匹配就不成功,不过,服务器那边还是照常执行。
  在测试的时候需要注意的事,有可能会发现改了nginx的配置,浏览器发出的跨域请求却没生效,这可能是因为浏览器cache的原因,只要清除浏览器的cache,再重新发起请求就好了。
  下一节CORS进阶之Preflight请求(二)会介绍CORS更高阶的内容,比如Preflight请求,Access-Control-Allow-Methods,Access-Control-Allow-Headers等。

 

文章来源:http://corsbook.rails365.net/467078

附一前端学习视频地址:https://www.rails365.net/playlists

posted on 2019-01-14 22:00  bijian1013  阅读(324)  评论(0)    收藏  举报

导航