【HTTP:什么是跨源资源共享(CORS)?详解:控制“跨源访问”】

跨源资源共享 CORS

  • [跨源资源共享 CORS (控制 跨源访问)](#跨源资源共享 CORS (控制 跨源访问))
    • [⑴ 浏览器 请求控制 (默认:允许同源请求、拒绝跨源请求)](#⑴ 浏览器 请求控制 (默认:允许同源请求、拒绝跨源请求))
    • [⑵ 浏览器的 两种跨源请求(简单请求、非简单请求)](#⑵ 浏览器的 两种跨源请求(简单请求、非简单请求))
    • [⑶ 跨源请求 CORS 相关的 HTTP 头信息 (表格)](#⑶ 跨源请求 CORS 相关的 HTTP 头信息 (表格))
    • [⑷ 带预检请求 OPTIONS 的 跨源请求流程(非简单请求、浏览器自动发起)](#⑷ 带预检请求 OPTIONS 的 跨源请求流程(非简单请求、浏览器自动发起))
    • [♣ 结束语 和 友情链接](#♣ 结束语 和 友情链接)


跨源资源共享 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 请求。
    • XMLHttpRequestfetch() 遵循同源策略 ,使用这些 API 的网页,只能对相同的来源 请求资源,除非跨源的响应里 包含正确的 CORS 头部,允许跨源访问。


⑵ 浏览器的 两种跨源请求(简单请求、非简单请求)

  • 浏览器把跨源请求 划分为两类简单请求非简单请求
    • :不管是哪类请求,最终都由浏览器自行判断,跨源请求也都是 浏览器自动完成。
  • 两种请求的区别
    • 发送的 请求 HTTP 头信息 会不同。
    • 请求步骤不同
      • a. 简单请求 :会直接发送 实际请求 ,因为是跨源的,会在头信息之中,增加一个Origin字段 表明 请求的来源地址 ,服务器根据这个地址决定 要不要同意请求。
        • 请求来源地址 不在允许范围内 :浏览器响应里 会没有 允许源 Access-Control-Allow-Origin字段 ,浏览器识别后 抛出错误,被 XMLHttpRequestonerror 回调函数捕获。这种错误,状态码可能无法判断,因为状态码可能是 200(请求成功)。
        • 请求来源地址 在允许范围内 : 服务器响应会多出几个 CORS 相关的头信息,表明 允许的信息
      • b. 非简单请求 : 在发送实际请求前,会先发一个"预检请求"OPTIONS 请求),看看服务器同意不同意该请求。如果同意了,再发实际请求。

  • 简单请求 需满足条件
    • 请求方法GETPOSTHEAD 之一。
    • 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-HeadersAccess-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-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma, 如果要访问其他头,则需要服务器设置本响应头。
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 标头 。
  • 浏览器 "自动发出 预检请求"

    • 当有必要的时候,浏览器会自动发出预检请求;所以在正常情况下,前端开发者不需要自己去发这样的请求,知道请求是怎样的即可。预检请求会在请求被标记为"需要预检"时进行,而对于简单请求 则不会进行 预检请求

  • : 浏览器预检请求 和 跨源服务器的响应的 简单示例。

    • 预检请求中:包括 Access-Control-Request-*Origin,而通过预检请求后, 实际请求中 只额外添加了 Origin
  • 第一步 ,js 代码中 有一个需要执行预检请求的 HTTP 请求。

    • 使用 POST 请求发送一个 XML 请求体,该请求包含了一个非标准的 HTTP X-PINGOTHER 请求标头。该请求的 Content-Typeapplication/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
      • 在搜索框输入查询,可以查询特性的浏览器支持,点击浏览器版本,还能看到具体支持情况;

  • 感谢:❤ 如果这篇文章 对您有帮助的话,可以点赞、评论、关注,鼓励下作者哦,谢谢小可爱们 ~╮( ̄▽ ̄)╭ ~
  • 欢迎指正: 如果发现 有需要更正的细节问题,欢迎指正。
  • 喜欢请收藏:喜欢本文 可收藏哦,方便快捷,会持续进行 ❶ 知识点更新 及 ❷ 修正错误。

  • 转载 请评论告知作者 并注明出处 ,Thanks♪(・ω・)ノ
    • 作者:Hey_Coder
    • 来源:CSDN
    • 版权声明:本文为博主原创文章,转载请附上博文链接!
相关推荐
一目Leizi4 小时前
Burp Suite物联网渗透测试实战指南:从HTTP到MQTT/CoAP的全面攻防
物联网·http·iphone
a程序小傲8 小时前
百度Java面试被问:HTTPS解决了HTTP什么问题?
java·后端·http·百度·面试
就叫飞六吧8 小时前
HTTP Basic认证完全指南
网络·网络协议·http
会头痛的可达鸭1 天前
Reqwest 库详细使用指南
http·rust·reqwest
橘子真甜~1 天前
C/C++ Linux网络编程11 - 数据加密与https协议
linux·服务器·网络·http·https·密码学·加密解密
jinxinyuuuus1 天前
抖音在线去水印:HTTP/2流量分析、反爬虫的对称与非对称加密
爬虫·网络协议·http
Fr2ed0m1 天前
HTTP vs HTTPS vs SSL/TLS:https协议全面解析(附HTTPS部署指南)
http·https·ssl
从负无穷开始的三次元代码生活1 天前
《图解HTTP》——浓缩讲解,快速了解HTTP
网络·网络协议·http
Kaede61 天前
MCP支持的通信协议:stdio、SSE、HTTP
网络·网络协议·http