为什么需要 CORS
过去,在互联网技术刚刚出现的时候,跨站请求伪造 (CSRF) 的问题时有发生。这些问题将虚假的客户端请求从受害者的浏览器发送到另一个应用程序。
例如,受害者登录了其银行的应用程序。然后,他们被诱骗在新的浏览器选项卡上加载外部网站。然后,外部网站使用受害者的 Cookie 凭证并将数据中继到银行应用程序,同时假装自己是受害者。然后,未经授权的用户就可以意外访问银行应用程序。
为了防止此类 CSRF 问题,所有浏览器现在都实施同源策略。
同源策略
如今,浏览器强制要求客户端只能向与客户端 URL 具有相同来源的资源发送请求。客户端 URL 的协议、端口和主机名都应与其请求的服务器相匹配。
例如,考虑以下 URL 与客户端 URL
http://store.aws.com/dir/page.html
的来源比较。URL | 结果 | 原因 |
http://store.aws.com/dir2/new.html | 来源相同 | 只有路径不同 |
http://store.aws.com/dir/inner/other.html | 来源相同 | 只有路径不同 |
https://store.aws.com/page.html | 来源不同 | 协议不同 |
http://store.aws.com:81/dir/page.html | 来源不同 | 端口不同(默认情况下,http:// 的端口号为 80) |
http://news.aws.com/dir/page.html | 来源不同 | 主机不同 |
因此,对于真正的用例来说,同源策略非常安全,但却不够灵活。
CORS
跨源资源共享 (CORS) 是同源策略的扩展。您需要它来与外部第三方共享授权的资源。例如,当您想从公共或授权的外部 API 中提取数据时,就需要 CORS。如果您想允许授权的第三方访问您自己的服务器资源,也需要 CORS。
什么是 CORS
复杂的应用程序通常会在其客户端代码中引用第三方 API 和资源。例如,您的应用程序可能使用浏览器从视频平台 API 提取视频,使用公共字体库中的字体,或显示来自国家天气数据库的天气数据,这些需要第三方资源的请求都会涉及到 CORS。
CORS(Cross-Origin Resource Sharing),跨源资源共享,是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
跨源 HTTP 请求的一个例子:运行在
https://domain-a.com
的 JavaScript 代码使用 XMLHttpRequest
来发起一个到 https://domain-b.com/data.json
的请求。出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。例如,
XMLHttpRequest
和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。你可以进一步了解什么情况下需要 CORS?
CORS 工作机制
在标准互联网通信中,浏览器向应用程序服务器发送 HTTP 请求,以 HTTP 响应的形式接收数据,然后显示数据。在浏览器术语中,当前浏览器 URL 被称为当前来源,而第三方 URL 则是跨源的。
当您发出跨源请求时,请求-响应过程如下:
- 浏览器在请求中添加来源标头,其中包含有关当前来源的协议、主机和端口的信息
- 服务器检查当前来源标头,然后使用请求的数据和
Access-Control-Allow-Origin
标头进行响应
- 浏览器查看访问控制请求标头并与客户端应用程序共享返回的数据
或者,如果服务器不想允许跨源访问,则会回复一则错误消息。
例如,假设有一个名为 https://news.example.com 的网站。该网站希望通过 partner-api.com 的 API 访问资源。
https://partner-api.com 的开发人员首先通过将 new.example.com 添加到允许的来源列表中,在服务器上配置跨源资源共享 (CORS) 标头。他们通过在服务器配置文件中添加以下行来做到这一点。
Access-Control-Allow-Origin: https://news.example.com
配置 CORS 访问权限后,news.example.com 可以从 partner-api.com 申请资源。对于每一项请求,partner-api.com 都会回复
Access-Control-Allow-Credentials : "true."
然后,浏览器知道通信已获得授权并允许跨域访问。如果您想授予对多个来源的访问权限,请使用逗号分隔的列表或通配符(如 *)向所有人授予访问权限。
什么是 CORS 预检请求?
在 HTTP 中,请求方法是客户端希望服务器执行的数据操作。常用的 HTTP 方法包括 GET、POST、PUT 和 DELETE。
在常规的跨源资源共享 (CORS) 交互中,浏览器会同时发送请求和访问控制标头。这些请求通常是 GET 数据请求,被认为是低风险的。
但是,有些 HTTP 请求被认为很复杂,需要服务器确认才能发送实际请求。这个预先批准过程被称为预检请求。
复杂的跨源请求
如果跨源请求使用以下任何一项,则会很复杂:
GET
、POST
或HEAD
以外的方法
Accept-Language
、Accept
或Content-Language
以外的标头
multipart/form-data
、application/x-www-form-urlencoded
或text/plain
以外的内容类型标头
因此,例如,删除或修改现有数据的请求被认为是复杂的。
预检请求的工作原理
如果需要,浏览器会创建预检请求。这是一个
OPTIONS
请求,如下所示。OPTIONS /data HTTP/1.1 Origin: https://example.com Access-Control-Request-Method: DELETE
浏览器在实际请求消息之前发送预检请求。服务器必须使用服务器愿意从客户端 URL 接受的跨源请求的相关信息来响应预检请求。服务器响应标头必须包含以下内容:
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Origin
下面给出了服务器响应示例。
HTTP/1.1 200 OK Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Origin: https://news.example.com Access-Control-Allow-Methods: GET, DELETE, HEAD, OPTIONS
预检响应有时会另外包含
Access-Control-Max-Age
标头。该指标为浏览器指定在其中缓存预检结果的持续时间(以秒为单位)。缓存允许浏览器在预检请求之间发送多个复杂的请求。在 max-age 指定的时间结束之前,它不必再次发送预检请求。CORS 相关问题及解决方案
目前比较常用的跨域解决方案有3种:
- JsonP 最早的解决方案,利用script标签可以跨域的原理实现。只能发起GET请求
- nginx反向代理 思路是:利用nginx反向代理把跨域为不跨域,支持各种请求方式,缺点:需要在nginx进行额外配置,语义不清晰
- CORS 规范化的跨域请求,安全可靠。
- 在服务端进行控制是否允许跨域,可自定义规则
- 支持各种请求方式
- 会产生额外的请求
优势:
缺点: