掌握 CORS 跨域请求,读这篇文章就够了

2023-02-10 0 1,041

在 Web 其间端分立构架商业模式下,布吕马(跨源)允诺归属于日常生活的基本上情形了。应用程序所致安全可靠考量,会管制 JavaScript(全称 JS)JAVA内发动跨源 HTTP 允诺,相混没这类管制。后端化解布吕马方式有许多,比如说WebSocket 协定布吕马、JSONP 允诺布吕马和布吕马数据共享 CORS 等。

01

CORS 概要

CORS 全称作 Cross-Origin Resource Sharing,被译为布吕马天然资源消解各式各样 API 的相混管制,以期在不相混(服务器)之间民泽,且保证布吕马传输的可靠性。

CORS 允诺并并非一类特定的 HTTP 允诺,反之亦然如前所述 HTTP 串行。CORS 允诺预设随身携带”or

origin: null origin: <scheme>://<hostname> origin: <scheme>://<hostname>:<port>

02

查阅应用程序的相容性

所推荐两个查阅应用程序优点、相容性和相容到具体内容别的版的中文网站。比如查阅各应用程序对 CORS 的全力支持情形,出访 URL 门牌号 https://caniuse.com/?search=CORS。如下表所示图右图:

03

相混与不相混的定义及举例说明

相混策略是由 Netscape 提出的两个著名的安全可靠策略,它是一类安全可靠约定。目h API 都遵循相混策略。相混策略也是应用程序基本上的安全可靠功能之一。

相混的定义:当两个 URL 使用的协定、域名(主机)和端口都相同的情形下,则称作两个 URL 相混,反之称两个 URL 不相混。下表整理了相混与不相混的 URL 示例说明:

URL A

URL B

结果

分析原因

https://www.example.com/a/

https://www.example.com/b/

相混

域名相同,只有路径相同

https://www.example.com/a/

https://www.example.com/a/c/

相混

域名相同,只有路径相同

http://www.example.com

http://www.example.com:80

相混

80 是 HTTP 协定预设端口

https://www.example.com

https://www.example.com:443

相混

443 是 HTTPS 协定预设端口

https://www.example.com

http://www.example.com

不相混

域名相同,协定相同

https://www.example.com

https://www.example.com:81

不相混

域名相同,端口相同

https://www.example.com

https://tool.example.com

不相混

主域名相同,二级域名相同

https://www.example.com

https://example.com

不相混

主域名相同,子域名相同

https://www.example.com

https://39.105.183.157

不相混

域名与 IP 相同

https://www.example.com

https://tool.box3.cn

不相混

完全相同的域名

http://www.example.com

http://localhost

不相混

完全相同的域名

04

常见的 CORS 出访控制场景

本例中,Nginx 服务器开启了 HTTP/2 协定,因此在 HTTP/2 二进制编码之前,必须将 HTTP 标头名称转换为小写。若允诺头、响应头中包含大写的字段名将被视为格式错误。

关键知识点

:如果 CORS 布吕马允诺是这三种方式之一:GET、POST 或 HEAD,那么在 HTTP 响应头中并不需要指明

access-control-allow-methods 字段的值。

4.1 简单允诺

什么是简单允诺?如果满足下述所有条件,才会被认定为”简单允诺”。请注意,对于”简单允诺”应用程序不会发动 CORS 预检允诺。

1、HTTP 允诺方式是以下三种之一:

GETPOSTHEAD

2、除了应用程序自动添加的首部字段(比如:connection,user-agent、date、referer 等)和 fetch 规范中定义的禁止使用的首部字段,和”proxy-“和”sec-“小写开头的首部字段。允许设置的首部字段集合为:

acceptaccept-languagecontent-languagecontent-type(见下列 3 )

3、content-type 的值是下列三者之一:

text/plainmultipart/form-dataapplication/x-www-form-urlencoded

4、允诺中的任意 XMLHttpRequest 对象均没注册任何事件监听器;XMLHttpRequest 对象可以使用 XMLHttpRequest.upload 属性出访。

5、允诺中没使用 ReadableStream 对象。

比如,请看两个 CORS 简单允诺的例

const xhr = newXMLHttpRequest();const url = https://api.box3.cn/example/simple; xhr.open(GET, url); xhr.send();

以下是应用程序发送给服务器的允诺报文(关键部分信息):

:method: GET :authority: api.box3.cn :scheme: https :path: /example/simple origin: https://tool.box3.cn user-agent: Mozilla/5.0 … …

以下是服务器返回的响应报文(关键部分信息):

:status: 200 OK server: nginx date: Thu, 17 Nov 2022 02:35:49 GMT content-type: application/json; charset=utf-8 content-length: 47 access-control-allow-origin: *

本例中,服务器返回的首部字段

access-control-allow-origin: * 表明,该天然资源可以被任意外部域出访或接受所有的允诺源。 access-control-allow-origin: *

如果只希望服务器允许来自 https://www.example.com 的出访,该首部字段的内容如下表所示:

access-control-allow-origin: https://www.example.com

关键知识点

:当响应的是附带身份凭证的允诺时(比如:Cookie),服务器必须明确

access-control-allow-origin 字段的值,而不能使用通配符”*”,否则应用程序的相混策略会阻止该允诺,并在控制台抛出错误。

4.2 预检允诺和实际允诺

首先,当允诺发生布吕马行为,且非简单允诺时,才会产生 CORS 预检允诺(CORS-preflight request)。其次与”简单允诺”相同的是,”预检允诺”是由应用程序自动发动的两个额外的 OPTIONS 允诺,以获知服务器是否授权后续的实际允诺(比如:XHR 或 Fetch API 发动的 HTTP 布吕马允诺)。其次,OPTIONS 允诺包含了两个重要的标头(首部字段)access-control-request-method 和 access-control-request-headers。

如下表所示是一段需要发动 HTTP 预检允诺的 JS 代码示例:

const xhr = new XMLHttpRequest(); xhr.open(GET, https://api.box3.cn/example/request); xhr.setRequestHeader(box3-token, 111-222-333-444); xhr.send();

如上代码使用 GET 允诺”发动之前,会先发动两个”预检允诺”。

掌握 CORS 跨域请求,读这篇文章就够了

下面是应用程序与服务器首次交互的报文信息,包括预检允诺头和预检响应头(备注:user-agent 省略了部分内容):

/* 预检允诺头 */ :method: OPTIONS :authority: api.box3.cn :scheme: https :path: /example/request access-control-request-method: GET access-control-request-headers: box3-token origin: https://tool.box3.cn user-agent: Mozilla/5.0 /* 预检响应头 */ :status: 204 No Content server: nginx date: Thu, 17 Nov 2022 02:35:35 GMT access-control-allow-headers: box3-token access-control-allow-origin: *

access-control-request-headers 告知服务器实际允诺随身携带的自定义标头,

access-control-allow-headers 告知客户端已全力支持的所有自定义标头,多个值之间以逗号分隔。

一般而言,服务器会对 OPTIONS 允诺的结果添加缓存时间。目的是,客户端减少了预检允诺交互的时间,同时也减少了对服务器的压力。比如说服务器在响应头中指定 access-control-max-age: 3600 表示该响应的有效时间为 3600 秒,也就是 1 小时。在这段时间内,应用程序不会对同一允诺再次发动预检允诺,而是直接发动实际情况。

添加预检允诺缓存之后,本例的预检响应头,最新内容如下表所示:

:status: 204 No Content server: nginx date: Thu, 17 Nov 2022 02:35:35 GMT access-control-allow-headers: box3-token access-control-allow-origin: * access-control-max-age: 3600

关键知识点:对于 OPTIONS 允诺,合法的 HTTP 状态码,应该定义在 2xx 范围内。比如说状态码设置为 200 或 204,都是正确的。

最后,待预检允诺通过之后,应用程序再发送实际允诺。下面是实际允诺的允诺头和响应头:

/* 实际允诺的允诺头 */ :method: GET :authority: api.box3.cn :scheme: https :path: /example/request box3-token: 111-222-333-444 origin: https://tool.box3.cn user-agent: Mozilla/5.0 /* 实际允诺的响应头 */ :status: 200 OK server: nginx date: Thu, 17 Nov 2022 02:35:35 GMT content-type: application/json; charset=utf-8 content-length: 45 access-control-allow-origin: *

4.3 简单允诺和凭据

预设情形下,对于 XMLHttpRequest 或 Fetch API 发动的布吕马允诺,应用程序不会发送 Cookie 信息。若要随身携带 Cookie,以 XMLHttpRequest 对象为例,需要设置属性 withCredentials 的值为 true。

本例中,站点 https://tool.box3.cn 内的 JS JAVA向 https://api.box3.cn 发动了两个简单的 GET 布吕马允诺,并附带了身份凭证 Cookie。JS 示例代码如下表所示:

const xhr = new XMLHttpRequest(); const url = https://api.box3.cn/example/simple_cookie; xhr.open(GET, url); xhr.withCredentials = true; xhr.send();

下面是应用程序与服务器交互的报文信息之关键部分(备注:user-agent 省略了部分内容):

/* 简单允诺的允诺头 */ :method: GET :authority: api.box3.cn :path: /example/simple_cookie :scheme: https cookie: access-token=100; origin: https://tool.box3.cn user-agent: Mozilla/5.0 /* 简单允诺的响应头 */ :status: 200 OK server: nginx date: Thu, 17 Nov 2022 02:52:07 GMT content-type: application/json; charset=utf-8 content-length: 45 access-control-allow-credentials: true access-control-allow-origin: https://tool.box3.cn

关键知识点

:服务器在响应头中必须指定

access-control-allow-credentials: true 来表明布吕马允诺允许随身携带 Cookie,否则仍然会被应用程序的 CORS 策略阻止。2、服务器在响应头中必须指定

access-control-allow-origin 字段特定的域,该标头的值不能设置为通配符 “*”,否则仍然会被应用程序的 CORS 策略阻止。

4.4 预检允诺和凭据

首先,两个完整的 CORS 预检允诺,是由应用程序自动完成的,这个动作对用户是无感知的。

其次,与”简单允诺和凭据”这小节整理的 CORS 策略知识点是一致的。那意味着,在 OPTIONS 允诺的响应头中必须明确指定

access-control-allow-credentials: true 和

access-control-allow-origin 字段特定的域,否则后续的实际允诺仍然会被应用程序的 CORS 策略阻止。

最后,在实际允诺的响应头中,也需要明确指定这两个字段且保持与 OPTIONS 相同的值。

关键知识点

:如果实际允诺的 HTTP 方法,非 GET、POST 或 HEAD,那么

access-control-allow-methods 字段的值不能设置为通配符”*”,应设置为特定的 HTTP 允诺方式名称,多个值之间以逗号分隔。

4.5 预检允诺与重定向

回顾 4.2 小节的关键知识点,预检允诺指的是 OPTIONS 允诺,且 HTTP 状态码定义在 2xx 范围内。因此,如果两个预检允诺发生了重定向,那么 HTTP 状态码一定大于 2xx,大多数应用程序将报告如下表所示错误:

Access to XMLHttpRequest at https://api.box3.cn/example/request_redirect fromoriginhttps://tool.box3.cn has been blocked by CORS policy: Response to preflight request doesnt pass access control check: Redirect is not allowed for a preflight request. 有两种方式可以规避上述报错行为:

有两种方式可以规避上述报错行为:

1、在服务端上去掉对预检允诺的重定向。

2、将该允诺优化成两个简单允诺。

05

常见的 4 种 CORS 错误

常见的 CORS 布吕马允诺错误,可能有以下 4 种情形(以下首部字段在服务器上配置):

1

access-control-allow-origin 配置不正确。

2、受信的 HTTP 方式

access-control-allow-methods 配置不全。

3、受信的首部字段

access-control-allow-headers 配置不全。

4、

access-control-allow-credentials 服务器与允诺方之间的凭证许可配置错误。

06

借助应用程序找错误

引发 CORS 错误的原因是布吕马允诺失败导致,并非 JS 代码层面出现的逻辑性 BUG。如果 JS 发动的 HTTP 允诺产生 CORS 错误,在 JS 代码层面无法获知具体内容是哪里出了问题,但是您可通过应用程序控制台获悉错误信息。比如在 Chrome 应用程序中,通过 F12 键启动开发者调试工具,在 Network 面板中了解具体内容的报错信息。如下表所示图右图:

掌握 CORS 跨域请求,读这篇文章就够了

07

认识这些 HTTP 允诺头和响应头

HTTP 允诺头字段

Header

说明

origin

表明预检允诺或实际允诺的源站。origin 的值只包括协定、域名、端口,不包含路径和参数。

access-control-request-method

出现于预检允诺中,其作用是,通知服务器在实际允诺中采用哪种 HTTP 方式。

access-control-request-headers

出现于预检允诺中,其作用是,通知服务器在实际允诺中使用哪些 HTTP 允诺头。

HTTP 响应头字段

Header

说明

access-control-allow-origin

出访。

access-control-expose-headers

其他响应头,通过该字段添加白名单。

access-control-allow-methods

对于预检允诺的响应,指明实际允诺允许使用哪些 HTTP 方式。

access-control-allow-headers

对于预检允诺的响应,指明实际允诺允许随身携带哪些 HTTP 头。

access-control-max-age

指定预检允诺的有效期,单位是秒。目的是减少发动预检允诺的次数。

access-control-allow-credentials

当设置为 true 时,告诉应用程序将响应公开给后端 JavaScript 代码。请注意,该值严格区分大小写,正确的写法是全小写。

作者:360技术

360技术工程

出处

:https://mp.weixin.qq.com/s/f7HvD1GpMqljClS_b7coUw

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务