彻底理解CORS请求

在最近的前后端分离项目开发过程中,频频遇到了CORS请求的问题。趁着思路还清晰,在这里做个备忘笔记。

本文部分内容参考自以下来源,在此深表感谢:
MDN官方文档 – 跨源资源共享(CORS)
跨域资源共享 CORS 详解 – 阮一峰的网络日志

什么是CORS?

根据MDN给出的定义:

跨源资源共享 (CORS) (或通俗地译为跨域资源共享)是一种基于HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的”预检”请求。在预检中,浏览器发送的头中标示有HTTP方法和真实请求中会用到的头。

举例来讲,假设前端页面运行在http://localhost:8080上,当用户访问前端资源,例如http://localhost:8080/index.html, http://localhost:8080/1.html 等,对这些资源的请求都不会涉及跨域问题。而当浏览器需要访问运行在http://localhost:8081上的后端资源时,因为端口号并不相同,浏览器便会执行跨域请求。

MDN给出的CORS请求示意图

前段定义中涉及到了一个关键的概念:Origin,也就是我们常说的域,或者源。我们可以将所有的HTTP请求归类为同源请求和跨域请求。我们通过以下几条来判断一个请求是否跨域:

  • 协议类型(HTTP/HTTPS)
  • 域名
  • 端口号

只有当前页面和被请求资源的以上三条都一致的时候才是同源请求,否则一律按跨域请求处理。这里需要注意的是,域名匹配时不会进行DNS转换,也就是说http://localhost:8080和http://127.0.0.1:8080同样不在一个域内。

CORS请求

根据被发送请求的具体内容不同,我们可以将HTTP请求分为简单请求和非简单请求。在处理简单请求时,浏览器不需要发送预检(Preflight)请求,而在处理复杂请求时,浏览器会首先向服务端发送OPTION预检请求来获取服务器是否允许以CORS方式访问资源。有关预检,我们将在稍后具体介绍。

简单请求

当HTTP请求方式为以下之一:

  • GET
  • HEAD
  • POST

且HTTP请求首部仅包括以下字段:

且Content-Type字段取(application/x-www-form-urlencodedmultipart/form-datatext/plain)三者之一时,该请求才可被视作简单请求。

请求和响应报文

# 请求报文
GET /resources/public-data/ HTTP/1.1 
Host: bar.other 
Accept-Language: en-us,en;q=0.5
Connection: keep-alive 
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html 
Origin: http://foo.example 
# 响应报文
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: application/xml

请求报文中的Origin以及相应报文的Access-Control-Allow-Origin共同构成了最基本的首部字段。Origin字段表明请求的来源,服务器返回的Access-Control-Allow-Origin指定了服务器接受的跨域访问源。*则表示接受所有外部跨域访问。