在我们进行网络应用开发的时候,如果用到了跨域网络请求,则不可避免的就会遇到http跨域网络请求 CORS的问题,今天就和大家来聊聊跨域网络请求中的CORS的那些事。
跨源资源共享(CORS)
CORS 是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(协议、域名或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
跨源 HTTP 请求的一个例子:运行在 https://domain-a.com 的 JavaScript 代码使用 XMLHttpRequest 来发起一个到 https://domain-b.com/data.json 的请求。
出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。例如,XMLHttpRequest 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。
CORS 机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch)使用 CORS,以降低跨源 HTTP 请求所带来的风险。
CORS使用
在了解了什么是CORS后我们就来聊聊怎么使用了, CORS是通过在服务端的相应头Header中通过 Access-Control-Allow-Origin 头来标识的, 格式如下:
Access-Control-Allow-Origin: <origin> | *
注意这里的允许的Origin的格式, 可是是具体的origin 或者通配符 * 表示允许所有请求。
origin格式: 协议://域名[端口]
这里的协议可以是 http, https, chrome-extension (chrome扩展里面进行的网络请求时的origin协议)
[端口] 如果是默认的 80 443 端口可以省略, 非默认端口就必须要指定允许的具体端口
origin示例
仅允许协议为https的域名dev.tekin.cn发送跨域请求: https://dev.tekin.cn
允许tekin.cn域名下的所有跨域请求:*.tekin.cn
chrome插件跨域请求CORS
允许chrome插件ID为 iflfjlikpkacloanbaaokocoafabjndg 的chrome插件发送跨域请求origin为: chrome-extension://iflfjlikpkacloanbaaokocoafabjndg
如何查看你的chrome插件ID?
在chrome的 扩展程序 菜单里面即可查看,如下:
附带身份凭证的请求 Access-Control-Allow-Credentials 与CORS通配符 * ?
Access-Control-Allow-Credentials 头指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容。当用在对 preflight 预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials。请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。
如果我们在响应header中开启了 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
对于附带身份凭证的请求(通常是 Cookie
),
这是因为请求的标头中携带了 Cookie
信息,如果 Access-Control-Allow-Origin
的值为“*
”,请求将会失败。而将 Access-Control-Allow-Origin
的值设置为 https://example.com
,则请求将成功执行。
Access-Control-Allow-Methods
Access-Control-Allow-Methods 标头字段指定了访问资源时允许使用的请求方法,用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Methods: <method>[, <method>]*
有关预检请求的示例已在上方给出,包含了将此请求头发送至浏览器的示例。
Access-Control-Allow-Headers
Access-Control-Allow-Headers 标头字段用于预检请求的响应。其指明了实际请求中允许携带的标头字段。这个标头是服务器端对浏览器端 Access-Control-Request-Headers 标头的响应。
Access-Control-Allow-Headers: <header-name>[, <header-name>]*
Access-Control-Expose-Headers
在跨源访问时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Expose-Headers 头将指定标头放入允许列表中,供浏览器的 JavaScript 代码(如 getResponseHeader())获取。
Access-Control-Expose-Headers: <header-name>[, <header-name>]*
例如:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
这样浏览器就能够通过 getResponseHeader 访问 X-My-Custom-Header 和 X-Another-Custom-Header 响应头了。
CORS go语言中使用示例
yml配置信息
以下是gin框架中的CORS中间件的使用示例代码, 其他的语言或者框架也都是一样的,就是将相关的响应Header头设置上即可。
总结:
1. 在服务端设置Access-Control-Allow-Origin时需要注意是否同时设置了Access-Control-Allow-Credentials : true ,如果是 则origin就不能使用*通配符,则必须指定对应的 origin;
2. origin的格式必须包含请求协议和域名,如 https://dev.tekin.cn
3. 在chrome插件里面的协议是 chrome-extension:// 域名对应的是chrome的插件ID。
4. origin中的端口如果是默认的80, 443端口,则可以省略, 如果是非默认端口,则必须包含完整的协议域名和端口信息。