跨源资源共享 CORS
- [跨源资源共享 CORS (控制 跨源访问)](#跨源资源共享 CORS (控制 跨源访问))
-
- [⑴ 浏览器 请求控制 (默认:允许同源请求、拒绝跨源请求)](#⑴ 浏览器 请求控制 (默认:允许同源请求、拒绝跨源请求))
- [⑵ 浏览器的 两种跨源请求(简单请求、非简单请求)](#⑵ 浏览器的 两种跨源请求(简单请求、非简单请求))
- [⑶ 跨源请求 CORS 相关的 HTTP 头信息 (表格)](#⑶ 跨源请求 CORS 相关的 HTTP 头信息 (表格))
- [⑷ 带预检请求 OPTIONS 的 跨源请求流程(非简单请求、浏览器自动发起)](#⑷ 带预检请求 OPTIONS 的 跨源请求流程(非简单请求、浏览器自动发起))
- [♣ 结束语 和 友情链接](#♣ 结束语 和 友情链接)
- 参考文档 :跨源资源共享 Cross-Origin Resource Sharing (CORS) - HTTP | MDN
- 本文根据 MDN 文档 归纳总结,更通俗易懂一点,了解更多详情,可看 MDN 原文档。
跨源资源共享 CORS (控制 跨源访问)
跨源资源共享 (又称 跨域资源共享 ,CORS:Cross-Origin Resource Sharing):是一种机制,该机制基于 HTTP 头,通过 设置 "一组 HTTP 标头字段",来控制 "跨源访问"。
-
CORS相关 HTTP 头 设置在 请求 和 响应的 HTTP 头信息中。跨源访问 相关的 HTTP 头信息字段 见 第⑶ 小节。- ❶ 请求中的 HTTP 头信息 ⇒ 提供网站 自身的信息,方便服务器判断,如 请求来源的网址、请求的方法、请求头 等。
- ❷ 响应中的 HTTP 头信息 ⇒ 提供服务器的响应信息,服务器根据请求内容 做出判断,允许哪些来源访问、允许的方法、允许的头、响应结果的最大缓存时间 等。
-
使用 跨源访问
CORS理由 :如,有时候,我们跨源访问别人家的资源时,要经过别人的同意,CORS可以让别人通过服务器设置 "是否同意访问请求,同意哪种请求",也可以让我们提供自身网站的一些信息,让对方服务器进行判断。 -
注 :跨源请求访问
CORS通信的整个过程,由 浏览器自动完成 ,跟 用户没关系。浏览器会自动判断 是不是跨源请求,要发什么 HTTP 请求信息,而服务器端只要实现了CORS相关设置,就可以跨源访问。
跨源资源共享 CORS 机制的用处 :服务器可以"控制 跨源访问",使跨源的数据传输 更安全。
- ❶ 谁能跨源访问 (服务器上设置)
- 服务器上可以设置允许 或 拒绝 "跨源访问请求" ,允许的话,别人就能跨源访问这个服务器的资源。这个设置的目的 就是告诉浏览器"谁可以访问 我服务器的资源?"
- 跨源:协议、域名、端口有一个不同,就算是跨源。
- ❷ 哪些跨源请求 是被允许的(服务器上设置)
- "预检"请求 :简单请求不需要预检请求,而非简单请求,服务器上可以设置 允许的请求 ,预检请求的目的就是确认 "服务器,你允许我的这种请求吗?"。
- 让浏览器使用 OPTIONS
HTTP方法,发起一个到服务器的 跨源资源的 "预检"请求 ,检查服务器是否允许要发送的真实请求。在预检中,浏览器发送的头中 有HTTP请求方法 、真实请求中会用到的请求头。 - 基本流程 :❶ 针对非简单请求,浏览器自动发起一个预检请求 (
OPTIONS方法 )⇒ ❷ 服务器响应:确定服务器是否允许该跨源请求 ⇒ ❸ 确认服务器允许 ⇒ ❹ 浏览器 发起实际的HTTP请求 。- 服务器端 对预检请求的响应 中,也可以通知客户端,是否需要携带身份凭证(如 Cookie 和 HTTP 认证相关数据)。
-
例 : 一个简单的 跨源请求。
- 网站 A (https://foo.example )的网页 想要访问 网站B ( https://bar.other) 的资源。
- 能不能实现 客户端和服务器之间的跨源访问,使用
CORS标头字段 来处理权限。 - 最简单的 跨源访问控制 :使用 浏览器-请求头信息的
Origin字段 和 服务器-响应头信息的Access-Control-Allow-Origin字段就能完成 最简单的跨源访问控制。 
-
如 网站A 的网页中 可能包含类似于下面的 JavaScript 代码:
-
第一步 ,网站 A 的网页 js 中 有跨源请求。
javascript
const fetchPromise = fetch("https://bar.other");
fetchPromise
.then((response) => response.json())
.then((data) => {
console.log(data);
});
- 第二步 ,浏览器处理跨源请求,发给服务器的请求头信息 添加
Origin头信息。- 简单的请求,浏览器会判定 不需要预检请求(
OPTIONS请求) ,直接发送 实际的HTTP请求,对于复杂的请求,浏览器也会自动发出预检请求,不需要手动设置。 Origin⇒ 指明 该请求来源网址 =http://foo.example。
- 简单的请求,浏览器会判定 不需要预检请求(
css
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
- 第三步 ,服务器收到请求后,根据
Origin给出响应。- ❶ 该资源可以被任意 外部资源 访问 ⇒
Access-Control-Allow-Origin: *。 - ❷ 限制 访问者只能是某网址:允许源= 具体网址 ⇒
Access-Control-Allow-Origin: https://foo.example。 - ⚠️注 :当响应的是 附带"身份凭证"的请求 时,
- ❶ 服务端必须明确 允许源= 具体网址
Access-Control-Allow-Origin: 具体网址,而不能使用通配符"*"。 - ❷ 服务器要明确 允许凭证
Access-Control-Allow-Credentials: true标头,否则,浏览器会拒绝浏览器的 这个响应,不会把这个响应提供给调用的网页。 
- ❶ 服务端必须明确 允许源= 具体网址
- ❶ 该资源可以被任意 外部资源 访问 ⇒
css
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[...XML Data...]
- 知识拓展:请求附带"身份凭证"时(通常是
Cookie)、服务器响应里的通配符 一般不能用- ⚠️ 针对 附带身份凭证的请求时,响应标头里的 访问控制 都不能使用 通配符
*,要使用针对该源的 特定值。 - 服务器不能 将 允许源
Access-Control-Allow-Origin的值设为通配符(*)- 而应将其设置为 特定的域 ,如:
Access-Control-Allow-Origin: https://example.com。
- 而应将其设置为 特定的域 ,如:
- 服务器不能 将 允许头
Access-Control-Allow-Headers的值设为通配符(*)- 而应将其设置为 特定标头名称的列表 ,如:
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
- 而应将其设置为 特定标头名称的列表 ,如:
- 服务器不能 将 允许方法
Access-Control-Allow-Methods的值设为通配符(*)- 而应将其设置为 特定请求方法名称的列表 ,如:
Access-Control-Allow-Methods: POST, GET
- 而应将其设置为 特定请求方法名称的列表 ,如:
- 服务器不能 将 允许的非普通头
Access-Control-Expose-Headers的值设为通配符(*)- 而应将其设置为 特定标头名称的列表 ,如:
Access-Control-Expose-Headers: Content-Encoding, Kuma-Revision
- 而应将其设置为 特定标头名称的列表 ,如:
- ⚠️ 针对 附带身份凭证的请求时,响应标头里的 访问控制 都不能使用 通配符
⑴ 浏览器 请求控制 (默认:允许同源请求、拒绝跨源请求)
① 浏览器 默认的请求控制 :✔️ 默认:允许同源请求、拒绝跨源请求。
- ❶ 同源请求⇒ 总是被浏览器允许,同源能确保基本的安全性。
- ❷ 跨源请求⇒ 浏览器默认"阻止跨源请求"。跨源请求 由
CORS相关的 HTTP 头信息控制(服务器上的设置,会指示浏览器是否允许跨源请求)。
② 浏览器的"同源策略" :浏览器默认阻止 "跨域请求" ,这是浏览器的安全策略,而非 HTTP 协议本身的限制。
- 浏览器限制 脚本内发起的 跨源 HTTP 请求。
- 如 XMLHttpRequest 和
fetch()遵循同源策略 ,使用这些 API 的网页,只能对相同的来源 请求资源,除非跨源的响应里 包含正确的CORS头部,允许跨源访问。 
- 如 XMLHttpRequest 和
⑵ 浏览器的 两种跨源请求(简单请求、非简单请求)
- 浏览器把跨源请求 划分为两类 :简单请求 、非简单请求 。
- 注:不管是哪类请求,最终都由浏览器自行判断,跨源请求也都是 浏览器自动完成。
- 两种请求的区别 :
- ① 发送的 请求 HTTP 头信息 会不同。
- ② 请求步骤不同 :
- a. 简单请求 :会直接发送 实际请求 ,因为是跨源的,会在头信息之中,增加一个
Origin字段 表明 请求的来源地址 ,服务器根据这个地址决定 要不要同意请求。- ❶ 请求来源地址 不在允许范围内 :浏览器响应里 会没有 允许源
Access-Control-Allow-Origin字段 ,浏览器识别后 抛出错误,被XMLHttpRequest的onerror回调函数捕获。这种错误,状态码可能无法判断,因为状态码可能是200(请求成功)。 - ❷ 请求来源地址 在允许范围内 : 服务器响应会多出几个
CORS相关的头信息,表明 允许的信息。
- ❶ 请求来源地址 不在允许范围内 :浏览器响应里 会没有 允许源
- b. 非简单请求 : 在发送实际请求前,会先发一个"预检请求" (
OPTIONS请求),看看服务器同意不同意该请求。如果同意了,再发实际请求。
- a. 简单请求 :会直接发送 实际请求 ,因为是跨源的,会在头信息之中,增加一个
- 简单请求 需满足条件
- ❶ 请求方法 :
GET、POST、HEAD之一。 - ❷ HTTP 头信息 :
- Accept、Accept-Language、Content-Language
- Range (仅使用单一区间头值。如, bytes=256- 或 bytes=127-255 )
- Content-Type 值仅限于下列三者之一:text/plain、multipart/form-data、application/x-www-form-urlencoded (这也是 表单
<form>标签中支持的 编码类型enctype属性值。)
- ❸ 如果请求是使用
XMLHttpRequest对象发出的,在返回的XMLHttpRequest.upload对象属性上没有注册任何事件监听器 ;也就是说,给定一个XMLHttpRequest实例xhr,没有调用xhr.upload.addEventListener(),以监听该上传请求。 - ❹ 请求中没有使用
ReadableStream对象。
- ❶ 请求方法 :
⑶ 跨源请求 CORS 相关的 HTTP 头信息 (表格)
① 跨源请求的 HTTP 请求头:
- 包括:请求来源地址、请求方法、请求头。
- 共同点 :除了 请求来源地址
Origin,其他都是以 访问控制 请求-*开头 :Access-Control-Request-*。
| 用途 | HTTP 请求头字段 | 允许值 |
|---|---|---|
1.请求来源地址 。 ✔️ 跨源请求时:Origin 标头字段总是被发送。 |
Origin |
Origin 值为源站 URL 。 它不包含 任何路径信息,只是服务器名称。 |
2. 请求方法 。 CORS 请求会用到哪些 HTTP 方法。 用于:预检请求。 |
Access-Control-Request-Method |
值为 HTTP 方法。 |
| 3. 请求头 。 实际请求 所携带的标头字段。 用于:预检请求。 | Access-Control-Request-Headers |
标头名。 多个值,逗号分隔。 |
② 跨源请求的 HTTP 响应头 :服务器为跨源请求 返回的 HTTP 响应头。
- 包括:允许源、允许方法、允许标头、指定的允许标头、最大缓存时间/有效期、是否允许带身份凭证。
- 共同点 :
- 基本上都是以 访问控制 允许-
*开头 :Access-Control-Allow-* - 只有两个以 访问控制 -
*开头 :Access-Control-*(Access-Control-Expose-Headers、Access-Control-Max-Age)
- 基本上都是以 访问控制 允许-
| 用途 | HTTP 响应头字段 | 允许值 |
|---|---|---|
| 1. 允许源 :允许哪些源 访问服务器资源。 ✔️ 跨源访问时,每次回应 都必定包含的字段。 | Access-Control-Allow-Origin |
`Access-Control-Allow-Origin: <origin> |
| 2. 指定的 允许标头 。 | Access-Control-Expose-Headers |
Access-Control-Expose-Headers: <header-name>[, <header-name>]* 多个值 逗号分隔。 在跨源访问时, XMLHttpRequest 对象的 getResponseHeader() 方法 只能拿到 6 个最基本的响应头 : Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma, 如果要访问其他头,则需要服务器设置本响应头。 |
| 3. 最大缓存时间/ 有效期(预检的响应结果) 。 用于:预检请求的响应。 | Access-Control-Max-Age |
Access-Control-Max-Age: <delta-seconds>,以秒为单位。 指定本次预检请求的 有效期 ,单位为秒。 在此期间,不用发出另一条预检请求。 |
| 4.允许的请求方法 。 用于:预检请求的响应。 | Access-Control-Allow-Methods |
支持的 HTTP 方法名。 该字段必需,多个方法 逗号分隔 。 表明服务器支持的所有跨域请求的方法。 注意,返回的是所有支持的方法 ,而不单是浏览器请求的那个方法。 避免多次"预检"请求。 |
| 5. 实际请求中 允许携带的标头字段 可以 作为响应的一部分 被暴露的头部。 | Access-Control-Allow-Headers |
标头名 ,多个值 用逗号分隔。 表明服务器支持的 所有头信息字段 ,不限于浏览器在"预检"中请求的字段。 如果浏览器请求包括 Access-Control-Request-Headers 字段,则 Access-Control-Allow-Headers 字段是必需的响应。 |
6. 实际的请求 是否可以使用身份凭证 credentials美/krəˈdenʃ(ə)lz/。 用于:预检请求的响应。 |
Access-Control-Allow-Credentials |
Access-Control-Allow-Credentials: true。 注 :此标头的有效值仅为 true(区分大小写)。 如果不需要凭据,完全省略此标头(而不是将其值设置为 false)。 CORS 请求 默认不发送 Cookie 和 HTTP 认证信息。 Cookie 遵循同源政策。 如果要把 Cookie 发到跨源服务器,必须进行两个设置 : ❶ 开发者必须在 AJAX 请求中打开 withCredentials 属性。 var xhr = new XMLHttpRequest(); xhr.withCredentials = true; ❷ 服务器要设置 Access-Control-Allow-Credentials: true。 |
⑷ 带预检请求 OPTIONS 的 跨源请求流程(非简单请求、浏览器自动发起)
-
CORS预检请求 :用于检查服务器 是否支持CORS协议,并且 是否允许使用 特定的请求方法、请求标头、访问来源网址。 -
预检请求(
OPTIONS请求) 使用的 HTTP 请求标头 :- ❶ 告知服务器 实际请求所使用的 HTTP 方法 :访问控制 请求方法
Access-Control-Request-Method标头。 - ❷ 告知服务器 实际请求所携带的 标头 :访问控制 请求头
Access-Control-Request-Headers标头。 - ❸ 告知服务器 请求 来源网址 (可选):来源网址
Origin标头 。
- ❶ 告知服务器 实际请求所使用的 HTTP 方法 :访问控制 请求方法
-
浏览器 "自动发出 预检请求":
- 当有必要的时候,浏览器会自动发出预检请求;所以在正常情况下,前端开发者不需要自己去发这样的请求,知道请求是怎样的即可。预检请求会在请求被标记为"需要预检"时进行,而对于简单请求 则不会进行 预检请求。
-
例 : 浏览器预检请求 和 跨源服务器的响应的 简单示例。
- 预检请求中:包括
Access-Control-Request-*、Origin,而通过预检请求后, 实际请求中 只额外添加了Origin。
- 预检请求中:包括
-

-
第一步 ,js 代码中 有一个需要执行预检请求的 HTTP 请求。
- 使用
POST请求发送一个 XML 请求体,该请求包含了一个非标准的 HTTPX-PINGOTHER请求标头。该请求的Content-Type为application/xml,且使用了自定义的请求标头,所以,该请求是非简单请求,需要首先发起"预检请求"。 (这个预检请求过程, 是浏览器自动判定和发起) - 这样的请求标头并不是 HTTP/1.1 的一部分,但通常对于 web 应用很有用处。
- 使用
javascript
const fetchPromise = fetch("https://bar.other/doc", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "text/xml",
"X-PINGOTHER": "pingpong",
},
body: "<person><name>Arun</name></person>",
});
fetchPromise.then((response) => {
console.log(response.status);
});
- 第二步:浏览器判断该请求 需要预检请求,自动发起预检请求(
OPTIONS请求) 。- 服务器根据 请求方法
Access-Control-Request-Method、请求头Access-Control-Request-Headers决定,该实际请求是否被允许。
- 服务器根据 请求方法
css
OPTIONS /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
- 第三步,服务器的响应 (针对 预检请求)。
- 服务器 允许的源 :访问控制 允许源
Access-Control-Allow-Origin。- 允许源-具体值 和
vary值 :允许源是具体网址时,Vary字段的值 必须包含Origin。- 如果服务端指定了具体的单个源(作为允许列表的一部分,可能会根据请求的来源而动态改变)而非通配符(
*),那么响应标头中的Vary字段的值 必须包含Origin - ⇒ 告诉客户端:服务器 对不同的
Origin,返回不同的内容 ⇒Vary: Accept-Encoding, Origin。
- 如果服务端指定了具体的单个源(作为允许列表的一部分,可能会根据请求的来源而动态改变)而非通配符(
- 允许源-具体值 和
- 服务器 允许的 HTTP 方法 :访问控制 允许方法
Access-Control-Allow-Methods。 - 服务器 允许的请求头 :访问控制 允许头
Access-Control-Allow-Headers。 - 服务器 响应结果最大缓存时间/ 有效期 :访问控制缓存最大时间
Access-Control-Max-Age。- 缓存响应结果,是为了在同一个 URL 下创建的请求中使用。为了缓存预检响应,浏览器使用一个特定的缓存,这个缓存 是与一般 HTTP 缓存分开的(浏览器管理的)。
- 单位为秒,默认值是
5秒。注:浏览器自身维护了一个最大有效时间,如果该标头字段的值超过了最大有效时间,将不会生效。
- 服务器 允许的源 :访问控制 允许源
css
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
- 第四步 ,通过了预检请求,被允许的话,浏览器发送 实际请求。
css
POST /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache
<person><name>Arun</name></person>
- 第五步 ,针对实际请求,服务器给出响应。
- 一旦服务器通过了"预检"请求,在预检的有效期内(
Access-Control-Max-Age),以后每次浏览器CORS跨源请求,就都跟简单请求一样,只会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
- 一旦服务器通过了"预检"请求,在预检的有效期内(
css
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some XML payload]
♣ 结束语 和 友情链接
- 快速搜索 :涉及知识点比较多,看过之后忘了很正常,想查询时,请按
Ctrl+F快速搜索关键字哦 (* ̄︶ ̄)。- 搜索后,多次按下 enter 键 或 点击搜索框后的 上下箭头,就能在关键词之间快速跳转。
- 友情链接
- MDN Web Docs
- 在搜索框输入查询,可以看详细知识点,也可以文章的最下方看到浏览器支持,点击浏览器版本,还能看到具体支持情况;
- Can I use... Support tables for HTML5, CSS3, etc
- 在搜索框输入查询,可以查询特性的浏览器支持,点击浏览器版本,还能看到具体支持情况;
- MDN Web Docs
- 感谢:❤ 如果这篇文章 对您有帮助的话,可以点赞、评论、关注,鼓励下作者哦,谢谢小可爱们 ~╮( ̄▽ ̄)╭ ~
- 欢迎指正: 如果发现 有需要更正的细节问题,欢迎指正。
- 喜欢请收藏:喜欢本文 可收藏哦,方便快捷,会持续进行 ❶ 知识点更新 及 ❷ 修正错误。
- 转载 请评论告知作者 并注明出处 ,Thanks♪(・ω・)ノ
- 作者:Hey_Coder
- 来源:CSDN
- 版权声明:本文为博主原创文章,转载请附上博文链接!
