本文原本于2020年6月17日发布在 RIVALSA 知识分享板块中,由于 RIVALSA 知识分享板块已下线,于2021年9月28日将本文转移至 RIVALSA 网络日志中。
出于安全考虑,浏览器通常会拒绝很多未经授权的非同源请求(例如XMLHttpRequest
),跨域资源共享(Cross-Origin Resource Sharing, CORS)通过添加 HTTP Header 的方式让浏览器得知当前请求是否已获得授权,从而允许或拒绝对应请求。CORS 的实现已经由浏览器自行完成了,我们要做的只是在 HTTP Header 中添加一些字段即可。跨域请求分为简单请求和非简单请求两种,同时满足以下条件的就属于简单请求,其他情况属于非简单请求。
- 请求方法是
GET
、POST
、HEAD
三种之一。- 请求的 HTTP Header 中只包含
Accept
、Accept-Language
、Content-Language
、Content-Type
、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
中的一个或几个。Content-Type
的值仅为,text/plain
、multipart/form-data
、application/x-www-form-urlencoded
三种之一。
在个别浏览器中会有比更加严格的要求,但这些更加严格限制只在个别的几个浏览器中有效。
简单请求
在发送简单请求时,会正常携带请求的信息,只是在请求的 HTTP Header 中添加一个 Origin
字段,值为当前页面的源(协议、域名、端口)信息。
服务器端可以根据请求的 HTTP Header 中的 Origin
字段的内容决定是否同意这次跨域请求,如果同意本次跨域请求需要根据具体情况在正常响应内容的同时,在响应的 HTTP Header 中添加如下字段中的一个或多个,如果不同意本次跨域请求则不添加如下 HTTP Header 即可。
字段 | 是否必选 | 描述 |
---|---|---|
Access-Control-Allow-Origin | 是 | 允许跨域访问的源,可以填写与请求的HTTP Header中的Origin 字段相同,表示允许对应的源进行跨域访问,也可以填写* 表示允许任何源跨域访问。 |
Access-Control-Allow-Credentials | 否,但当请求 HTTP Header 中携带 Cookie 信息时必选 | 允许响应中携带 Cookie 时,此值为 true ,否则应省略此字段。当请求 HTTP Header 中携带 Cookie 信息时,此值必须为 true 。 |
Access-Control-Expose-Headers | 否 | 响应的 HTTP Header 中除了 Cache-Control 、Content-Language 、Content-Type 、Expires 、Last-Modified 、Pragma 之外允许携带的字段。如果这里没有设置对应的值,则客户端无法拿到对应的字段。 |
通常客户端在发送 ajax 请求时也会有一个
withCredentials
属性,其值为true
时表示发送请求时允许携带 Cookie,否则表示不允许携带 Cookie。当请求地址为同源时,无论此值设置为什么,均允许携带 Cookie。
非简单请求
如果要发送的跨域请求不满足简单请求的条件,则会发起非简单请求。对于非简单请求浏览器会首先发起预检请求来判断服务器是否允许跨域通信。预检请求使用的请求方法为 OPTIONS
,其中并不包含请求信息,只是在预检请求的 HTTP Header 中含有如下字段,这些信息是浏览器根据正式发起跨域请求时的参数自动添加的,不需要开发者手动设置。
字段 | 描述 |
---|---|
Origin | 发起请求的源(协议、域名、端口)信息。 |
Access-Control-Request-Method | 正式发起跨域请求使用的请求方法。 |
Access-Control-Request-Headers | 正式发起跨域请求时需要携带的 HTTP Header 字段,有多个 HEADER 时用英文逗号分隔。 |
服务器端收到预检请求后会根据请求中以上 HTTP Header 的内容决定是否同意这次跨域请求,如果同意本次跨域请求需要根据具体情况在预检请求的响应的 HTTP Header 中添加如下字段中的多个,如果不同意本次跨域请求则不添加如下 HTTP Header 即可。
字段 | 是否必选 | 描述 |
---|---|---|
Access-Control-Allow-Origin | 是 | 允许跨域访问的源,可以填写与请求的 HTTP Header 中的 Origin 字段相同,表示允许对应的源进行跨域访问,也可以填写 * 表示允许任何源跨域访问。 |
Access-Control-Allow-Methods | 是 | 服务器在正式响应跨域请求时支持的方法,如果允许多种方法可用英文逗号分隔。此值中必须包含但不限于预检请求中 Access-Control-Request-Method 的值。 |
Access-Control-Allow-Headers | 否,但当遇预检请求的 HTTP Header 中包含 Access-Control-Request-Headers 字段时必选 |
服务器在响应跨域请求时支持传递的HTTP Header,如果允许在请求中传递多个HTTP Header可用英文逗号分隔。此值中必须包含但不限于预检请求中Access-Control-Request-Headers 的值。 |
Access-Control-Allow-Credentials | 否,但当请求 HTTP Header 中携带 Cookie 信息时必选 | 允许正式响应中携带 Cookie 时,此值为 true ,否则应省略此值。当请求 HTTP Header 中携带 Cookie 信息时,此值必须为 true 。 |
Access-Control-Max-Age | 否 | 本次预检请求的有效期,单位为秒。在此时间内的跨域请求直接按照简单请求来处理,不需要发出另一条预检请求。 |
当浏览器发送了预检请求并收到同意跨域的响应后,会再次按照简单请求的方式发送跨域请求,请求与响应的规则与简单请求相同。
对于简单请求,只是在请求和响应的 HTTP Header 中新增一个或几个字段,其他信息还是正常传递的。对于预检请求,在请求与响应中只有与 CORS 相关的字段,并不包含其他信息。
(正文完)
本作品著作权归属 Rivalsa 所有,除非 Rivalsa 明确许可您使用,否则任何个人或组织不得以任何方式直接或间接的复制、伪造、转载、摘编、翻印、改编、演出或以其他方式使用本作品。
匿名 - 2021-09-29 12:48:09 举报
不错,感觉通俗易懂。