Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORS 简单请求+预检请求(彻底理解跨域) #62

Open
amandakelake opened this issue Dec 19, 2018 · 9 comments
Open

CORS 简单请求+预检请求(彻底理解跨域) #62

amandakelake opened this issue Dec 19, 2018 · 9 comments
Labels
Web 网络、性能、优化类

Comments

@amandakelake
Copy link
Owner

amandakelake commented Dec 19, 2018

概述

当一个资源从与该资源本身所在的服务器不同的域、协议、端口请求一个资源时,资源会发起一个跨域 HTTP 请求。同源策略参考浏览器的同源策略 | MDN

出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求,XMLHttpRequest和Fetch API,只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件

对于浏览器限制这个词,要着重解释一下:不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了

CORS概述

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。

服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
2ec957a3-b220-49c5-8fa1-59a82a030e89

简单请求

不会触发CORS预检的请求称为简单请求,满足以下所有条件的才会被视为简单请求,基本上我们日常开发只会关注前面两点

  1. 使用GET、POST、HEAD其中一种方法
  2. 只使用了如下的安全首部字段,不得人为设置其他首部字段
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type 仅限以下三种
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
    • HTML头部header field字段:DPR、Download、Save-Data、Viewport-Width、WIdth
  3. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问
  4. 请求中没有使用 ReadableStream 对象

预检请求

需预检的请求要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响

下面的请求会触发预检请求,其实非简单请求之外的就会触发预检,就不用记那么多了

  1. 使用了PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH方法
  2. 人为设置了非规定内的其他首部字段,参考上面简单请求的安全字段集合,还要特别注意Content-Type的类型
  3. XMLHttpRequestUpload 对象注册了任何事件监听器
  4. 请求中使用了ReadableStream对象

以下是一个发起预检请求的例子
发起请求的origin与请求的服务器的host不同,而且根据上面的条件判断,触发了预检
66210125-a3e0-422a-8def-79b1e32028c7
a4e8f139-9ac9-4087-847c-bfe8e356695b

请求附带身份凭证 -> cookies

如果发起请求时设置withCredentials 标志设置为 true,从而向服务器发送cookie, 但是如果服务器端的响应中未携带Access-Control-Allow-Credentials: true,浏览器将不会把响应内容返回给请求的发送者

对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为*, 必须是某个具体的域名

注意,简单 GET 请求不会被预检;如果对此类带有身份凭证请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页

完整请求流程

dd16afcc-c37b-4269-a427-4e42c6e5773f

参考

HTTP访问控制(CORS) | MDN
Server-Side Access Control | MDN
Using CORS - HTML5 Rocks
浏览器的同源策略 | MDN

@gaochundong
Copy link

nice

@pzs-pzs
Copy link

pzs-pzs commented Apr 3, 2020

GET不是不会触发么?为啥说“其实非简单请求之外的就会触发预检,就不用记那么多了”

@gaoryrt
Copy link

gaoryrt commented Apr 13, 2020

GET不是不会触发么?为啥说“其实非简单请求之外的就会触发预检,就不用记那么多了”

如果使用 get,但是人为设置了其他头,或者是规定外的 Content-Type 就不是简单请求了,就会发预检了。

@xuyangzhou
Copy link

为什么预检options请求中已经添加了Access-Control-Allow-Origin等响应头了,这样已经达到了服务器允许那些来源的请求,其他请求还需要加Access-Control-Allow-Origin等响应头

@quixote1304629
Copy link

options预检请求,会存在跨域问题么

@studylss
Copy link

studylss commented Apr 1, 2022

options预检请求,会存在跨域问题么

作者开头就说了,不一定是浏览器限制了跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器给拦截了。

@tangchao0106
Copy link

nice

@ciloi
Copy link

ciloi commented Apr 6, 2023 via email

1 similar comment
@ciloi
Copy link

ciloi commented Dec 31, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Web 网络、性能、优化类
Projects
None yet
Development

No branches or pull requests

9 participants