本文主要是介绍HTTP 第六章 跨资源共享(CORS),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
跨资源共享(CORS)
跨源资源共享(CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
例如:运行在 https://baidu.com 的js代码使用 XMLHttpRequest 向 https://google.com 发起请求。
出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。

CORS 机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行。
CORS 规范要求
对可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。
服务器确认允许之后,才发起实际的 HTTP 请求。
在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(例如 Cookie 和 HTTP 认证相关数据)
跨域请求的分类
简单请求
简单请求不会触发 CORS 预检请求。
如果请求满足所有下述条件,则该请求可视为简单请求:
- 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
- 除了被用户代理自动设置的标头字段(例如 Connection、User-Agent 或其他在 Fetch 规范中定义为禁用标头名称的标头),允许人为设置的字段为 Fetch 规范定义的对 CORS 安全的标头字段集合。该集合为:
- Accept
- Accept-Language
- Content-Language
- Content-Type 所制定的请求头仅限于下列三者
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- Range (只允许简单的范围标头值 如 bytes=256- 或 bytes=127-255)
- 如果请求是使用 XMLHttpRequest 对象发出的,在返回的 XMLHttpRequest.upload 对象属性上没有注册任何事件监听器;也就是说,给定一个 XMLHttpRequest 实例 xhr,没有调用 xhr.upload.addEventListener(),以监听该上传请求。
- 请求中没有使用 ReadableStream 对象
例如: 站点 https://foo.example 网页应用想要访问 https://bar.other 的资源
fetch('https://bar.other/test', {method: 'GET',
}).then(res => res.json()).then(res => console.log(res));
此操作实行了客户端和服务器之间的简单交换,使用 CORS 标头字段来处理权限:

服务端返回的 Access-Control-Allow-Origin
标头的 Access-Control-Allow-Origin: *
值表明,该资源可以被任意外源访问
const express = require('express');const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));app.get('/test', (req, res) => {res.header('Access-Control-Allow-Origin', '*');res.send({code: 0,data: 'hello world'});
});app.listen(3000, () => {console.log('server is running on port 3000');
});
如果 https://bar.other 的资源持有者想限制他的资源只能通过 https://foo.example 来访问,可以这样做:Access-Control-Allow-Origin: https://foo.example
预检请求
当不满足简单请求的条件时,浏览器会发送预检请求“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
"预检请求“ 的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
如下是一个需要执行预检请求的 HTTP 请求:
fetch('https://bar.other/test', {method: 'POST',headers: {'X-PINGOTHER': 'pingpong','Content-Type': 'application/xml',}
}).then(res => res.json()).then(res => console.log(res));
上面的代码发送 POST 请求,该请求包含了一个非标准的 HTTP X-PINGOTHER 请求标头。这样的请求标头并不是 HTTP/1.1 的一部分,但通常对于 web 应用很有用处。另外,该请求的 Content-Type 为 application/xml,且使用了自定义的请求标头,所以该请求需要首先发起“预检请求”。

下面是服务端和客户端完整的信息交互。
首次交互是预检请求/响应
浏览器根据上面的 JavaScript 代码片断所使用的请求参数,向服务端发送一个预检请求,该请求的请求头如下:
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-Request-Method 告知服务器,实际请求将使用 POST 方法。
标头字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求标头字段:X-PINGOTHER 与 Content-Type。
服务器根据预检请求的请求头信息,决定是否允许该实际请求。
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
对于预检请求,不需要响应任何的消息体,只需要在响应头中添加:
Access-Control-Allow-Origin
:和简单请求一样,表示允许的源Access-Control-Allow-Methods
:表示允许的后续真实的请求方法Access-Control-Allow-Headers
:表示允许改动的请求头Access-Control-Max-Age
:告诉浏览器,多少秒内,对于同样的请求源、方法、头,都不需要再发送预检请求了
预检请求完成之后,发送实际请求
附带身份凭证的请求
XMLHttpRequest 或 Fetch,可以基于 HTTP cookies 和 HTTP 认证信息发送身份凭证。默认情况下,跨域请求并不会附带cookie。以通过简单的配置就可以实现附带cookie。
// xhr
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;// fetch api
fetch(url, {credentials: "include"
})
当一个请求需要附带cookie时,无论它是简单请求,还是预检请求,都会在请求头中添加cookie
字段。
服务器响应时,需要明确告知客户端:服务器允许这样的凭据。需要在响应头中添加:Access-Control-Allow-Credentials: true
。
在响应附带身份凭证的请求时:
- 服务器不能将 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
在跨源访问时,浏览器只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Expose-Headers
头让服务器把允许浏览器访问的头放入白名单,例如:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
这样JS就能够访问指定的响应头了。
这篇关于HTTP 第六章 跨资源共享(CORS)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!