CORS 跨域资源共享

407 阅读3分钟

跨域资源共享(CORS,英文全称[Cross-Origin Resource Sharing]),基本思路就是使用自定义的 HTTP 头部,允许浏览器和服务器相互了解,以确定请求或响应应该成功还是失败。

同源策略

浏览器的安全基石是“同源策略”,它是浏览器最核心也最基本的安全功能。

同源策略的目的:为了保护用户信息的安全,防止恶意的网站窃取数据。

同源策略的限制

  • CookieLocalStorageIndexDB 无法读取。
  • DOM 无法获得。
  • Ajax 请求发送后,结果被浏览器拦截了。

所谓“同源”,指的就是“协议”、“域名”和“端口”三者都相同。

下面的表格将举例说明哪些属于同源,哪些属于非同源。

683000432-5bcd1e36bd004_fix732.png

跨域

什么是跨域呢?

跨域就是指向非同源地址,跨域请求就是向非同源地址发送请求。

默认情况下,Ajax 只能向同源地址发送请求,但它发送跨域请求时,并不是请求不能发送出去,而是请求能够发送出去,服务器端也能接收到请求并正常返回结果,只是结果被浏览器拦截了。

跨域解决方案

JSONP

HTML 中有三个允许跨域加载资源的标签:

  • <img src="" alt="">
  • <link rel="stylesheet" href="">
  • <script src=""></script>

JSONP 正是利用 script 标签可以跨域加载资源这一特性,从其他源获取 JSON 数据;JSONP 需要服务器做支持才可以。

JSONP 的优点: 简单易用,兼容性好。

JSONP 的缺点:

  1. 只支持 GET 方式的请求,具有局限性;
  2. 由于是从不同的域拉取可执行代码,可能会遭受 XSS 攻击;
  3. 不好确定 JSONP 请求是否失败。

CORS

CORS 原理解析

简单请求:

对于简单的请求,即没有自定义头部的 GET 或者 POST 请求,并且它的请求体是 text/plain 类型。这样的请求在发送时,浏览器会添加一个额外的头部,叫 Origin,它包含发送请求的页面的源(协议、域名和端口),服务器会根据这个 Origin 中的值,决定是否同意这次请求。

如:

Origin: http://www.nczonline.net

如果服务器决定响应该请求,那么这个回应的头信息中应该发送 Access-Control-Allow—Origin,包含相同的源或者 "*"(资源是公开的)。

非简单请求:

对于非简单请求的CORS请求,在正式通信之前,会先向服务器发送一个“预检”请求。

预检请求使用 OPTIONS 方法发送并包含了以下头部:

Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
  • Origin:发送请求的页面的源(协议、域名和端口);
  • Access-Control-Request-Method:请求希望使用的方法;
  • Access-Control-Request-Headers:[可选]该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段。

在预检请求发送后,服务器可以确定是否允许这种类型的请求,确认允许请求,就可以做出回应。服务器会通过在响应中发送如下头部来与浏览器沟通:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 1728000
  • Access-Control-Allow-Origin:包含与请求的页面相同的源(协议、域名和端口)或者 "*"
  • Access-Control-Allow-Methods:允许的方法;
  • Access-Control-Request-Headers:服务器允许的头部;
  • Access-Control-Max-Age:缓存预检请求的秒数(即20天);

Access-Control-Max-Age:该字段可选,如果设置了该项,预检请求返回后,结果会按响应指定的时间缓存一段时间。换句话说,就是在有效期内,之后的请求不需要发送预检请求。

一旦服务器通过了预检请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样。

CORS 支持所有类型的 HTTP 请求,是跨域 HTTP 请求的根本解决方案,比 JSONP 的方式更强大。