为什么需要cors?
出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。
预检请求
对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求),浏览器必须使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证 相关数据)。
不会触发预检请求的叫“简单请求”(见MDN)。
一个简单的访问控制
使用 Origin(请求头) 和 Access-Control-Allow-Origin(响应头) 可以完成最简单的访问控制。
附带身份凭证的请求
- 请求中发送了Cookies,如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true,浏览器将不会把响应内容返回给请求的发送者。
- CORS 预检请求不能包含凭据。预检请求的 响应 必须指定 Access-Control-Allow-Credentials: true 来表明可以携带凭据进行实际的请求。
在响应附带身份凭证的请求时:
- 服务器不能将 Access-Control-Allow-Origin 的值设为通配符“*”,而应将其设置为特定的域,如:Access-Control-Allow-Origin: example.com。否则请求会失败。
- 服务器不能将 Access-Control-Allow-Headers 的值设为通配符“*”,而应将其设置为首部名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
- 服务器不能将 Access-Control-Allow-Methods 的值设为通配符“*”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET
HTTP 响应首部字段
Access-Control-Allow-Origin
- 指定允许访问该资源的外域 URI
- 若设置为*,则表示允许来自所有域的请求
Access-Control-Expose-Headers
- 跨源访问时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到一些最基本的响应头,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,这样浏览器就能够通过 getResponseHeader 访问 X-My-Custom-Header 和 X-Another-Custom-Header 响应头了。
Access-Control-Max-Age
- 指定了 preflight 请求的结果在多少秒内有效
Access-Control-Allow-Credentials
- 是否允许浏览器读取 response 的内容
Access-Control-Allow-Methods
- 指明实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Headers
- 指明实际请求中允许携带的首部字段。
HTTP 请求首部字段
Origin
- 表明预检请求或实际请求的源站。
- origin 参数的值为源站 URI,不包含任何路径信息,只是服务器名称。
Access-Control-Request-Method
- 用于预检请求,将实际请求所使用的 HTTP 方法告诉服务器。
Access-Control-Request-Headers
- 用于预检请求,将实际请求所携带的首部字段告诉服务器。