跨域请求后如何访问 iframe.contentDocument 以获得响应?

Hri*_*sto 5 javascript iframe cross-domain same-origin-policy

我成功地将文件从 发送localhost:8888localhost:8080(生产中的不同域),但在传输完成后我无法读取 HTTP 响应。

未捕获的安全错误:无法从“HTMLIFrameElement”读取“contentDocument”属性:阻止来源为“ http://localhost:8888 ”的框架访问来源为“ http://localhost:8080 ”的框架。请求访问的框架将“document.domain”设置为“localhost”,但正在访问的框架没有设置。两者都必须将“document.domain”设置为相同的值才能允许访问。

为了发送文件,为了兼容性支持,我试图让它适用于<form>基于文件上传;没有XHR根据。这是基本的 HTML 结构:

<form target="file-iframe" enctype="multipart/form-data" method="POST" action="invalid">
  <input type="file" id="file-input" class="file-input" title="select files">
</form>
<iframe src="javascript:false;" id="file-iframe" name="file-iframe"></iframe>
Run Code Online (Sandbox Code Playgroud)

要将<iframe>元素插入到 DOM 中,我执行以下操作:

document.domain = document.domain;
var domainHack = 'javascript:document.write("<script type=text/javascript>document.domain=document.domain;</script>")';

var html = '<iframe id="file-iframe" name="file-iframe"></iframe>';
var parent = document.getElementById('wrapper');
var iframe = UTILS.createDomElement(html, parent);
iframe.src = domainHack;

UTILS.attachEvent(iframe, 'load', function(e) {

  // this throws the above SecurityError
  var doc = iframe.contentDocument || iframe.contentWindow.document;

  // do other stuff...
});
Run Code Online (Sandbox Code Playgroud)

在提交表单之前,我将 的action属性设置为<form>目标跨域 URL:

action="http://localhost:8080/"
Run Code Online (Sandbox Code Playgroud)

提交后<form><iframe>'sload事件被触发,我尝试访问<iframe>'s 内容以读取 HTTP 响应。但是,这样做会引发上述错误,因为这是一个跨域请求,并且我无权访问 的内容<iframe>

我认为document.domain黑客会起作用,但错误消息告诉我,即使我将's属性设置为变量,它iframe也没有将域设置为,这似乎执行了。localhostiframesrcdomainHack

关于我可能做错了什么有什么想法吗?我如何设置document.domainlocalhost<iframe>其父页面(即当前页面)。


我已经阅读了几个 StackOverflow 问题、一些 MDN 文章以及 Google 上的其他随机结果,但我无法让它发挥作用。我已经看过的一些东西:

Hri*_*sto 3

在深入研究并尝试解决这个问题之后,我终于找到了一个似乎对我有用的解决方案。但是,这并不是我问题的准确答案。

总之,我正在致力于支持<form>基于文件上传。对于不支持文件上传的浏览器XHR,我们只能采用传统的<form>提交方式,使用隐藏<iframe>来避免页面刷新。该表单将重新加载操作重定向到 hide ,然后在文件传输后将<iframe>HTTP 响应写入到 的正文中。<iframe>

由于同源政策,以及我提出这个问题的原因,我无权访问 的<iframe>内容。s的同源策略<iframe>限制从一个源加载的文档或脚本如何与来自另一源的资源交互。由于我们无法访问 的<iframe>文档(文件上传 HTTP 响应写入的位置),因此服务器将返回一个重定向响应,服务器将在该响应中附加上传响应 JSON。当<iframe>加载时,JS 会解析出响应 JSON,并将其写入到 的<iframe>body 中。最后,由于重定向到相同的源,我们可以访问 的<iframe>内容:)

非常感谢jQuery 文件上传器;他们做了所有艰苦的工作;)

https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads

配置...

JS

function setupForm() {

  // form is declared outside of this scope
  form = document.createElement('form');
  form.setAttribute('id', 'upload-form');
  form.setAttribute('target', 'target-iframe');
  form.setAttribute('enctype', 'multipart/form-data');
  form.setAttribute('method', 'POST');

  // set the 'action' attribute before submitting the form
  form.setAttribute('action', 'invalid');
};

function setupIframe() {

  // iframe is declared outside of this scope
  iframe = document.createElement('iframe');

  /*
   * iframe needs to have the 'name' attribute set so that some versions of
   * IE and Firefox 3.6 don't open a new window/tab 
   */
  iframe.id = 'target-iframe';
  iframe.name = 'target-iframe';

  /*
   * "javascript:false" as initial iframe src to prevent warning popups on
   * HTTPS in IE6
   */
  iframe.src = 'javascript:false;';
  iframe.style.display = 'none';

  $(iframe).bind('load', function() {
    $(iframe)
      .unbind('load')
      .bind('load', function() {
        try {

          /*
           * the HTTP response will have been written to the body of the iframe.
           * we're assuming the server appended the response JSON to the URL,
           * and did the redirect correctly
           */
          var content = $(iframe).contents().find("body").html();
          response = $.parseJSON(content);

          if (!response) {
            // handle error
            return;
          }

          uploadFile(...); // upload the next file
        }
        catch (e) {
          // handle error
        }
      });
  });

  /*
   * insert the iframe as a sibling to the form. I don't think it really
   * matters where the iframe is on the page
   */
  $(form).after(iframe);
};
Run Code Online (Sandbox Code Playgroud)

HTML - 重定向页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script type="text/javascript">
      // grabs the JSON from the end of the URL
      document.body.innerHTML = decodeURIComponent(window.location.search.slice(1));
    </script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

剩下要做的唯一一件事是将action属性设置<form>为我们将上传发送到的跨域 URL:

form.setAttribute('action', 'http://sub.example.com:8080/upload?id=ab123');
form.submit();
Run Code Online (Sandbox Code Playgroud)

与此同时,服务器上……

// send redirect to the iframe redirect page, where the above HTML lives
// generates URL: http://example.com/hack?%7B%22error%22%3Afalse%2C%22status%22%3A%22success%22%7D
response.sendRedirect("http://example.com/hack?{\"error\":false,\"status\":\"success\"}");
Run Code Online (Sandbox Code Playgroud)

我知道这是一个巨大的黑客攻击,可以绕过 s 的同源策略<iframe>,但它似乎有效,而且我认为它的跨浏览器兼容性非常好。我尚未在所有浏览器中测试它,但我会抽出时间进行测试,并发布更新。