CORS

CORS 是一种机制,允许在浏览器中运行的代码(例如 Javascript)向与代码发起源不同的域发出请求。

Swift 支持对容器和对象进行 CORS 请求。

CORS 元数据仅保存在容器中。给定的值适用于容器本身以及容器内的所有对象。

支持的头部包括:

元数据

使用

X-Container-Meta-Access-Control-Allow-Origin

允许进行跨域请求的源,用空格分隔。

X-Container-Meta-Access-Control-Max-Age

源保存预检结果的最长时长。

X-Container-Meta-Access-Control-Expose-Headers

在实际请求响应中暴露给用户代理(例如浏览器)的头部,用空格分隔。

除了在容器元数据中设置的值之外,还可以使用 strict_cors_modecors_allow_origincors_expose_headersproxy-server.conf 中配置一些集群范围内的值。有关更多信息,请参阅 proxy-server.conf-sample

在浏览器发出实际请求之前,它可能会发出一个 预检请求。预检请求是一个 OPTIONS 调用,用于验证源是否允许发出请求。事件顺序如下:

  • 浏览器向 Swift 发出 OPTIONS 请求

  • Swift 根据允许的源向浏览器返回 200/401

  • 如果返回 200,浏览器将向 Swift 发出“实际请求”,即 PUT、POST、DELETE、HEAD、GET

当浏览器收到实际请求的响应时,它只会暴露 Access-Control-Expose-Headers 头部中列出的头部。默认情况下,Swift 会为该头部返回以下值:

  • “简单的响应头部”,如 http://www.w3.org/TR/cors/#simple-response-header 上所述

  • 头部 etagx-timestampx-trans-idx-openstack-request-id

  • 所有元数据头部(容器的 X-Container-Meta-* 和对象的 X-Object-Meta-*

  • X-Container-Meta-Access-Control-Expose-Headers 中列出的头部

  • 使用 cors_expose_headers 选项在 proxy-server.conf 中配置的头部

注意

对符号链接对象的 OPTIONS 请求将仅响应符号链接的选项,请求不会重定向到目标对象。因此,如果符号链接的目标对象位于具有 CORS 设置的另一个容器中,则响应将不会反映这些设置。

示例 Javascript

要查看 CORS Javascript 的实际效果,请下载 测试 CORS 页面(源代码如下)。将其托管在 Web 服务器上,并记下您将用于请求该页面的协议和主机名(源),例如 https://

找到您想要查询的容器。显而易见的是,托管此容器的 Swift 集群应该具有 CORS 支持。将测试页面的源附加到容器的 X-Container-Meta-Access-Control-Allow-Origin 头部:

curl -X POST -H 'X-Auth-Token: xxx' \
  -H 'X-Container-Meta-Access-Control-Allow-Origin: https://' \
  http://192.168.56.3:8080/v1/AUTH_test/cont1

此时,容器现在可供托管在 https:// 上的 CORS 客户端访问。在浏览器中打开测试 CORS 页面。

  1. 填充 Token 字段

  2. 使用容器或对象的 URL 填充 URL 字段

  3. 选择请求方法

  4. 点击提交

如果请求成功,您应该看到响应头部和主体。如果出现问题,响应状态将为 0。

测试 CORS 页面

示例跨站点测试页面位于项目源代码树 doc/source/test-cors.html 中。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Test CORS</title>
  </head>
  <body>

    Token<br><input id="token" type="text" size="64"><br><br>

    Method<br>
    <select id="method">
        <option value="GET">GET</option>
        <option value="HEAD">HEAD</option>
        <option value="POST">POST</option>
        <option value="DELETE">DELETE</option>
        <option value="PUT">PUT</option>
    </select><br><br>

    URL (Container or Object)<br><input id="url" size="64" type="text"><br><br>

    <input id="submit" type="button" value="Submit" onclick="submit(); return false;">

    <pre id="response_headers"></pre>
    <p>
    <hr>
    <pre id="response_body"></pre>

    <script type="text/javascript">
      function submit() {
          var token = document.getElementById('token').value;
          var method = document.getElementById('method').value;
          var url = document.getElementById('url').value;

          document.getElementById('response_headers').textContent = null;
          document.getElementById('response_body').textContent = null;

          var request = new XMLHttpRequest();

          request.onreadystatechange = function (oEvent) {
              if (request.readyState == 4) {
                  responseHeaders = 'Status: ' + request.status;
                  responseHeaders = responseHeaders + '\nStatus Text: ' + request.statusText;
                  responseHeaders = responseHeaders + '\n\n' + request.getAllResponseHeaders();
                  document.getElementById('response_headers').textContent = responseHeaders;
                  document.getElementById('response_body').textContent = request.responseText;
              }
          }

          request.open(method, url);
          if (token != '') {
              // custom headers always trigger a pre-flight request
              request.setRequestHeader('X-Auth-Token', token);
          }
          request.send(null);
      }
    </script>

  </body>
</html>