在同一域上的IE9中访问contentDocument时出现"Access is Denied"错误

6

简短/通用版:

我正在开发一个应用程序,它(不幸的是,因为其他原因)在每个页面的顶部将document.domain设置为“真实”域名的子字符串:对于像sub.app.local这样的子域,document.domain = "app.local"。我还动态创建一个iframe并将其添加到页面中。 iframe加载与父页面位于同一服务器上的文件。稍后,一些Javascript需要访问iframe的contentDocument属性。

这在现代浏览器中没有问题,但由于此bug,它会在IE9中出现问题。 (请参见顶部答案以获得有关此的良好解释。)据我所知,新于IE9的所有浏览器都会自动继承通过编程方式创建的iframes的document.domain,因此这是IE9特定的。由于我的方案具有某些独特要求(tldr:iframe src需要更改),因此上述帖子中的答案对我无效。

较长/针对应用程序的版本:

我正在使用FineUploader在运行在IIS上的应用程序中,将文件上传到S3。在现代浏览器中一切正常,但即使在按照文档(支持IE9和更早版本)配置iframeSupport.localBlankPagePath选项后,IE9支持也会给我带来麻烦。我有点困惑!

  • 我已经在FineUploader 4.0.3和5.0.3上尝试过。
  • 在IE11中一切正常。如果我在F12控制台中切换文档模式为9,它就会出问题。
  • 我没有打开CORS支持,因为我没有从不同的域加载任何内容。
  • 我在与我的页面相同的域上有一个虚拟/空白文件。
  • 我可以看到(在网络面板中)来自AWS的HTTP 303响应,其中包括Location键和指向我的空白文档的值。如果我将整个URL粘贴到地址栏中,则页面会正常加载且为空白。
  • 我尝试添加X-Frame-Options SAMEORIGIN到服务器的响应标头,如此处所建议的那样,但没有帮助。

我在控制台中收到的错误是:

[Fine Uploader 5.0.3] Error when attempting to access iframe during handling of upload response (Access is denied.
)
[Fine Uploader 5.0.3] Amazon likely rejected the upload request



更新

我已经确定,它不能直接使用的原因是应用程序在页面加载时设置了document.domain,并且它与(子集)location.host不同。 FineUploader的303重定向机制到空白页面运行正常,但iframe的document.domain与父级不同(由于IE9不继承该属性),因此访问被拒绝。

实际的S3上传是有效的。 只是验证上传的最后一步失败了。

这是我的客户端代码:

FineUploader.js

var fu = namespace('App.FineUploader');
fu.DocId;
fu.ClientDeployId;
fu.viewModel;
fu.defaultAttachmentEndpoint = "https://s3.amazonaws.com/App.UploadBucket";
fu.FineUploaderController = "FineUploaderDocAttachment";

fu.delete = function (documentAttatchmentID, attachmentID) {
    var data = documentAttatchmentID;
    $.ajax({
        type: "POST",
        url: App.BaseUrl + "/api/" + fu.FineUploaderController + "/delete",
        data: JSON.stringify(data),
        success: function () {
            $('#fineUploader' + attachmentID).fineUploader('reset');
            $('#fineUploader' + attachmentID).show();
            $('#aDownloadfineUploader' + attachmentID).html('');
            $('#aDownloadfineUploader' + attachmentID).hide();
            $('#aDeletefineUploader' + attachmentID).hide();
        },
        dataType: 'json',
        contentType: 'application/json'
    })
}

fu.lockAll = function () {
    $('.fineUploader').hide();
    $('a[id^="aDeletefineUploader"]').hide();
}

fu.init = function (sID) {
    $('#' + sID).fineUploaderS3({
        request: {
            endpoint: fu.defaultAttachmentEndpoint,
            accessKey: "[key]",
            params: {
                documentid: $('#' + sID).attr('documentid'),
                attachmentid: $('#' + sID).attr('attachmentid')
            }
        },
        signature: {
            endpoint: App.BaseUrl + "/api/" + fu.FineUploaderController + "/signtureHandler"
        },
        uploadSuccess: {
            endpoint: App.BaseUrl + "/api/" + fu.FineUploaderController + "/success"
        },
        iframeSupport: {
            localBlankPagePath: App.BaseUrl + "/Scripts/FineUploader/4.0.3/html/blank.html"
        },
        objectProperties: {
            key: function (fileId) {
                var keyRetrieval = new qq.Promise(),
                    filename = $('#' + sID).fineUploader("getName", fileId);
                var documentid = $('#' + sID).attr('documentid');
                var attachmentid = $('#' + sID).attr('attachmentid');
                var data = { name: filename, documentId: documentid, attachmentId: attachmentid }
                $.ajax({
                    type: "POST",
                    url: App.BaseUrl + "/api/" + fu.FineUploaderController + "/preUpload",
                    data: JSON.stringify(data),
                    success: null,
                    dataType: 'json',
                    contentType: 'application/json'
                }).done(function (data) {
                    keyRetrieval.success(data.key);
                }).fail(function () {
                    keyRetrieval.failure();
                });
                return keyRetrieval;
            }
        },
        validation: {
            itemLimit: 1
        },
        chunking: {
            enabled: true
        }
    }).on('error', function (event, id, name, errorReason, xhrOrXdr) {
        alert(qq.format("Error on file number {} - {}.  Reason: {}", id, name, errorReason));
    }).on('complete', function (event, id, name, response) {
        if (fu.FineUploaderController === "FineUploaderDocLibraryAttachment") {
            $('#' + sID).fineUploader('reset');
            App.Contracts.Create.ReloadImageLibraryList(true);
            App.Contracts.Create.HideLoader();
        } else {
            $('#aDownload' + sID).attr('href', response.url);
            $('#aDownload' + sID).html(response.name);
            $('#aDownload' + sID).show();
            $('#aDelete' + sID).show();
            $('#aDelete' + sID).attr('onclick', 'App.FineUploader.delete(' + response.daId + ',' + sID.replace('fineUploader', '') + ');');
            $('#' + sID).hide();
        }
    }).on('submitted', function () {
        if (fu.FineUploaderController === "FineUploaderDocLibraryAttachment") {
            App.Contracts.Create.ShowLoader();
        }
    });
}

你尝试过给IIS授予管理员权限吗?这是错误的常见来源。 - Sifu
我相当确定这不是问题所在,因为父应用程序目前正在我们的生产服务器上运行,没有任何问题。(除非这是与这里的某个相关文件的特定权限问题。) - Nate Barbettini
你使用过原生的IE9吗,还是只用了IE11中的怪异模式切换?存在差异,并且已知怪异模式会引起问题。 - Mark Feltner
我认为您需要确保返回的iframe的content-type也是text/html - Mark Feltner
你需要展示客户端代码才能获得有用的帮助。同时,你还需要告诉我们文件是否成功传输到S3。 - Ray Nicholus
@RayNicholus 抱歉,我是 Stack Overflow 的新手!我已经发布了我的客户端代码(如果您需要更多/其他片段,请告诉我),并更新了我的调试进展。 - Nate Barbettini
2个回答

2

如果有人在FineUploader之外看到这个答案,我是基于这个思路来实现我的解决方案的。

为了实现这个功能,我稍微修改了FineUploader的blank.html:

<head>
<script type="text/javascript">
    // Provide a mechanism to override document.domain
    // inside the iframe via this url syntax: blank.html?[args]#domain.com
    if (location.hash.substring(1).length > 0)
        document.domain = location.hash.substring(1);
</script>
</head><body></body>

这使我可以在生成 iframe 时从父页面中提供正确的 document.domain 值。微调 FineUploader 配置对象:
$.fineUploaderS3({
[snip]
    iframeSupport: {
        localBlankPagePath: App.BaseUrl + "/Scripts/FineUploader/4.0.3/html/blank.html#" + document.domain
    },
[/snip]
}

这似乎不会影响由AWS添加的参数。在该应用程序中,我们仍在使用FineUploader 4.0.3,但最新版本也应该可以正常工作。

简而言之; 它起作用了!在IE11文档模式下测试过,并且还在本机IE9上测试过。


请注意,iframeSupport选项在IE10+中不需要,因此被忽略。实际上,这仅用于IE7-9。 - Ray Nicholus
@RayNicholus 说得有道理,我也是这么想的。感谢您的澄清。 - Nate Barbettini

1
错误提示表明,由 iframe 提供的页面与托管上传程序的页面确实不属于同一域。或者是某些插件/扩展程序引起了问题。根据错误信息,Fine Uploader 简单地无法访问 iframe 中的任何内容,这发生在 iframe 的域与托管上传程序的框架/页面的域不匹配时。

是的,原来页面中早期的一些Javascript确实将域设置为其他内容(基本域,没有子域部分)。我在下面发布了我的解决方案。我不确定它是否是“最佳”的,但它可以工作。 - Nate Barbettini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接