前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对象存储COS跨域CORS问题小结

对象存储COS跨域CORS问题小结

原创
作者头像
吴硕卫
修改2020-12-23 12:09:21
9.1K0
修改2020-12-23 12:09:21
举报
文章被收录于专栏:腾讯云存储专家服务

跨源资源共享(CORS)

CORS(Cross-origin resource sharing) 中文名称"跨域资源共享",由于安全原因,Web 应用程序默认情况只能在同源(协议、域名和端口)的情况下向服务器获取数据。

主要有以下三种行为会受到限制:

Cookie、LocalStorage 和 IndexDB 无法读取。 DOM 无法获得。禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。 AJAX 请求不能发送(XMLHttpRequest)。

但是在日常的业务开发中,我们是需要经常访问跨域资源的。CORS 机制允许 Web 应用进行跨源访问,需要浏览器和服务器同时支持。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

1. 请求分类

CORS 将请求分成了两类:简单请求(Simple Request)和非简单请求。其中非简单请求会触发预检请求(Preflight Request)。满足以下两大条件的请求就属于简单请求。

请求方法为下列方法之一 GET HEAD POST 请求的 HTTP 的头部信息不超出以下几种字段 Accept Accept-Language Content-Language Content-Type -> { text/plain, multipart/form-data, application/x-www-form-urlencoded }undefinedDPR Downlink Save-Data Viewport-Width Width

这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。

凡是不满足上面两个条件,就属于非简单请求。例如 COS V5 版本的 XML 接口中,当 Content-Type 为 application/xml 时就会触发 CORS 预检请求。

2. 简单请求

对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是在头信息之中,增加一个 Origin 字段。下面我们先看一下 COS 服务器端对于跨域访问 CORS 设置中的各参数的配置作用,并给出结果图。

2.1 浏览器端

浏览器在发起跨域请求时会自动向 HTTP Header 添加一个额外的请求头字段:Origin。Origin 字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。

另外还有三个 Sec-Fetch-* 开头的字段,这是一个新的草案 Fetch Metadata Request Headers

Sec-Fetch-Mode 代表请求的模式,主要有 cors、navigate、nested-navigate、no-cors 等等。

Sec-Fetch-Site 代表请求的来源是同源还是跨域。

2.2 COS 服务器端

Access-Control-Allow-Origin -> 来源 Origin

作用:COS 服务端允许跨域的源。

如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,被 XMLHttpRequest 的 onerror 回调函数捕获。

Access-Control-Allow-Methods -> 操作 Methods

作用:HTTP 请求方法的限制

这个参数应该还是比较容易理解的,允许浏览器的 CORS 请求会用到哪些 HTTP 方法。

Access-Control-Expose-Headers -> Expose-Headers

作用:允许浏览器端能够获取相应的 header 值

CORS 请求时,如果服务器端没有设置对应的Access-Control-Expose-Headers 字段,浏览器通过请求响应后的 Header 如下,比如我们非常熟悉的 x-cos-request-idETag 等头部无法在浏览器中无法获取到。

在 COS CORS 设置中把Expose-Headers置为*再来看一下结果

就可以拿到 COS 服务器端返回的全部 Header 字段了

Access-Control-Allow-Credentials

作用:是否允许发送 Cookie

这个头部在 COS CORS 设置中并没有对应的选项,如果要发送 Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。当 Access-Control-Allow-Origin 指定源后,COS 服务器端会自动设置该字段为 true。

当然,如果需要使用该特性,开发者也必须在请求时打开withCredentials属性。

3. 非简单请求

预检请求是在发送实际请求前,客户端先发送一次 OPTIONS 方法请求到服务器端来确认请求是否通过,可以避免跨域请求对服务器的用户数据造成影响。

如何判断是否会发送预检请求可以参考第一部分的请求分类。

3.1 浏览器端

预检请求用的请求方法是 OPTIONS,表示这个请求是用来询问的。

当然也需要带上 Origin 字段。

除了 Origin 字段,预检请求的头信息还包括两个特殊字段 Access-Control-Request-MethodAccess-Control-Request-Headers

Access-Control-Request-Method

该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法。如 PUT、POST、GET 等。

Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段。

如上图在请求的时候加上了自定义头部 X-Custom-Header = shuoweiwu,所以触发了预检请求。

Provisional headers are shown 字面意思是"显示了临时报文头",代表请求被阻塞,未收到响应,说明 请求并没有发出去

 看下控制台报错和 Network 里的请求详情,是由于预检请求报错 403,被浏览器拦截了。

代码语言:txt
复制
Access to XMLHttpRequest at 'https://xxxxxxxxx-xxxxxxxxxx.cos.ap-guangzhou.myqcloud.com/test/3.jpg' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

3.2 COS 服务器端

Access-Control-Allow-OriginAccess-Control-Allow-Methods 同简单请求一样,分别用于判定请求源和请求方法,需要注意的是以下两个配置项。

Access-Control-Allow-Headers -> Allow-Headers

作用:表示服务器允许请求中携带的请求头部字段。

比如上面预检请求中的 X-Custom-Header 头部。

Access-Control-Max-Age -> 超时 Max-Age

作用:指定本次预检请求的有效期,单位为秒。

如果设置 超时 Max-Age 为 0,则浏览器发送请求的时候始终都会先发送 OPTIONS 预检请求。

COS 中的 CORS 配置:

预检请求:

实际请求:

超时 Max-Age 设置为 600 时,只有在第一次请求时发送了 OPTIONS 预检请求。

4. 跨域重定向

当跨域请求被重定向时,中间服务器返回的 CORS 相关的响应头应当与最终服务器保持一致。 任何一级的 CORS 失败都会导致 CORS 失败。即需要满足每一级的 CORS 都能够通过验证。



浏览器会直接访问重定向后的地址,可以跟随多次重定向。但是需要注意的一点就是:

重定向后请求头 Origin 字段会被设为 null

重定向请求:

重定向后 Origin 字段被置为 null,导致重定向后的跨域请求失败:

解决方法有以下两种:

  1. 设置每一级的 Access-Control-Allow-Origin 字段为 *
  2. 设置重定向后的 Access-Control-Allow-Origin 字段为 null

5. 跨域的 src 属性

具有 src 属性的 HTML 标签都可以跨域

<link>,<script>,<img> 等标签是可以直接进行跨域访问的,但是不会产生跨域头。

6. 常见问题总结

当然这里最常见的问题就是已经配置好了跨域头,用 curl 测试生效,但是在前端页面访问的时候没有生效,看 Network 的请求头里确实是没有 CORS 的相关字段。

由于img标签是可以直接进行跨域访问的,在请求 COS 前,img标签加载了同样的图片,因为img加载在前,等到访问 COS 中的资源的时候,浏览器直接使用了缓存,缓存中是没有跨域头的,导致了跨域失败。

如何判定有可能是命中了浏览器缓存?

  1. 请求的时候存在 Provisional headers are shown字段,如上所述,代表请求没有发出来,有可能是命中了浏览器缓存。
  2. AJAX 请求的 COS 资源的 request id 与 img、script 标签等一致。
  3. Network 栏里的 Remote Address 为空或者 size 中的值为(disk cache)。
  4. 打开调试器的 disable cache 选项后观察看下能否复现。

使用场景以及命中浏览器缓存后的解决方案:

  1. 直接访问COS源站
    1. 使用Cache-Control头部关闭缓存。如在 COS 上传的时候加上该头部Cache-Control:no-cache,或者复制该资源的时候加上该头部。如果对象数量不是很多,可以直接在COS控制台点开该对象详情,设置自定义Headers
    2. 设置 <img> 标签的 crossorigin 属性的值为 anonymous,强制图片每次请求都使用 XHR 的 CORS 请求。
    3. AJAX 请求图片的时候加上随机参数。
  2. 访问CDN域名,CDN回源到COS 如果只在COS侧配置了跨域,但是没有在CDN配置的话,由于CDN会缓存住第一次访问的请求,第一次请求没有跨域的话CDN会缓存住这个头部,可能会导致后面的跨域请求失败了,所以这种场景下建议在CDN侧下发跨域配置。还有一种场景是一个COS域名对应多个CDN域名时,也是由于CDN的缓存问题,可能会导致各个CDN域名表现不一致,这种场景也建议在CDN配置跨域头部。CDN 自定义响应头配置
    1. 仍然可以使用COS的Cache-Control头部关闭缓存,并且刷新对应的CDN的URL。
    2. 设置 <img> 标签的 crossorigin 属性的值为 anonymous,强制图片每次请求都使用 XHR 的 CORS 请求。
    3. AJAX 请求图片的时候加上随机参数。

ps: 其中设置 <img> 标签的 crossorigin 属性的方式是可以使用本地缓存的,但是可能有些浏览器是不支持 crossOrigin 的。

Vary头部 -> COS对跨域的进一步支持

Vary头部的使用场景是本地浏览器通过多个域名访问同一个URL,带上Vary头部后浏览器会缓存住不同Origin的请求,这个头部COS侧会尽快安排上,丰富产品的特性。

其他常见问题:

  • 重定向后跨域失败 -> 判断是否满足每一级的 CORS 验证
  • 浏览器无法获取到如ETag等字段 -> 参考上面 CORS 的 Expose Header 的配置

Reference:

跨源资源共享(CORS)

跨域资源共享 CORS 详解

✋🏼🔥 CS Visualized: CORS

总结-使用 CORS 解决跨域问题

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 跨源资源共享(CORS)
    • 1. 请求分类
      • 2. 简单请求
        • 2.1 浏览器端
        • 2.2 COS 服务器端
      • 3. 非简单请求
        • 3.1 浏览器端
        • 3.2 COS 服务器端
      • 4. 跨域重定向
        • 5. 跨域的 src 属性
          • 6. 常见问题总结
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档