7

为了解决 JavaScript 的跨域安全问题,我正在实现以下方法

在域 [abc.com]

在域上abc.com,我有一个名为main_page.html. 它的代码如下——

<script>
function SendMsg(id)
{
   frames["invisible_iframe"].location = "http://xyz.com/invisible_iframe.html#"+id;
}
</script>
<body>
  <input type="button" id="Test" value="Call iFrame" onclick="SendMsg(this.id);">
  <iframe src="ttp://xyz.com/visible_iframe.html" name="visible_iframe" height="250" width="500"></iframe>
  <iframe name="invisible_iframe" height="0" width="0" style="display:none;"></iframe>
</body>

在域 [ xyz.com ]

在域上xyz.com,我有一个名为visible_iframe.html. 它的代码如下——

<script>
function Hi()
{
   alert("Hi there!");
}
</script>
<body>
  <h1>Visible iFrame on xyz.com
  <iframe name="d2_invisible_iframe" id="d2_invisible_iframe" class="iFrame" src="http://xyz.com/invisible_iframe.html" height="310" width="520"></iframe>
</body>

现在我想Hi()invisible_iframe.html(在同一个域上)访问该函数

的代码invisible_iframe.html如下

<script>
var sActionText = "";
function CheckForMessages()
{
   if(location.hash != sActionText)
   {
     sActionText = location.hash;
     var sAction = "";
     var oSplitActionText = sActionText.split("#");
     sAction = oSplitActionText[1];
     if (sAction == "Test")
     {
        parent.Hi();
     }
   }
}

setInterval(CheckForMessages, 200); 
</script>
<body>
  <h1>Invisible iFrame on xyz.com</h1>
</body>

我正在使用隐藏的 iFrame,visible_iframe.html因为我不想更改 visible_iframe.html 的 URL。

现在我希望当单击按钮时main_page.html,我会收到警报消息。但这不会发生。在 Firefox 中它说 -访问属性“嗨”的权限被拒绝

奇怪的是当我把parent.Hi();外部CheckForMessages()功能,该Hi()功能可以访问并显示警告框。

我应该怎么做才能解决这个问题?

4

9 回答 9

4

为什么不使用easyXDM?这个库应该让你的生活变得非常轻松,并有助于在处理跨域问题时避免安全限制。特别是如果您可以控制这两个域。

easyXDM 是一个 Javascript 库,使您作为开发人员能够轻松解决同源策略设置的限制,从而使跨域边界的通信和公开 javascript API 变得容易。

[这是最好且易于使用的 API 之一]可用于 Web 应用程序之间的跨域通信。easyXDM易于使用、轻量级、灵活、编写高质量的代码等。我强烈认为如果您要继续跨域场景,那么您应该采用健壮的跨域 API,例如 easyXDM。

[easyXDM vs PostMessage Transport?] easyXDM 将使用 PostMessageTransport 方法,如果浏览器启用了此功能,例如(IE8+,Opera 9+,Firefox 3+,Safari 4+,Chrome 2+),它会使用不同的不受支持的浏览器的传输方法,例如(Firefox 1-2 - 使用 FrameElementTransport)将根据需要使用其他传输方法,例如 FlashTransport、NameTransport 和 HashTransport)。

这显然使得easyXDM在浏览器支持方面更胜一筹,特别是旧浏览器。


演示使用 easyXDM 的跨域访问(Domain1 [abc.com] 调用远程域 [xyz.com] 上的方法):


*在域 [abc.com] - 主域*

    <script type="text/javascript">
        /**
         * Request the use of the JSON object
         */
        easyXDM.DomHelper.requiresJSON("../json2.js");
    </script>
    <script type="text/javascript">
        var remote;
        window.onload = function(){
            /**
             * When the window is finished loading start setting up the interface
             */
            remote = new easyXDM.Interface(/** The channel configuration */{
                /**
                 * Register the url to hash.html, this must be an absolute path
                 * or a path relative to the root.
                 * @field
                 */
                local: "/hash.html",
                /**
                 * Register the url to the remote interface
                 * @field
                 */
                remote: "http://YOUR.OTHER.DOMAIN/YOUR_APPLICATION/YourRemoteApplication.html",
                /**
                 * Register the DOMElement that the generated IFrame should be inserted into
                 */
                container: document.getElementById("embedded")
            }, /** The interface configuration */ {
                remote: {
                    remoteApplicationMethod: {},
                    noOp: {
                        isVoid: true
                    }
                },
                local: {
                    alertMessage: {
                        method: function(msg){
                            alert(msg);
                        },
                        isVoid: true
                    }
                }
            },/**The onReady handler*/ function(){
                /**
                 * Call a method on the other side
                 */
                remote.noOp();
            });
        }

        function callRemoteApplicationMethod(Value1, Value2){
            remote.remoteApplicationMethod(Value1, Value2, function(result){
                alert("Results from remote application" + result);
            });
        }


    </script>

在身体里

<input type="button" onclick="callRemoteApplicationMethod(3,5)" value="call remoteApplicationMethod on remote domain"/>

现在在您的远程域端,您需要按如下方式定义您的远程客户端

*在域 [ xyz.com ] - 远程域*

这应该进入页面 YOUR_APPLICATION/YourRemoteApplication.html

    <script type="text/javascript">
        /**
         * Request the use of the JSON object
         */
        easyXDM.DomHelper.requiresJSON("../json2.js");
    </script>
    <script type="text/javascript">
        var channel, remote;
        /**
         * When the window is finished loading start setting up the channel
         */
        window.onload = function(){

            /**
             * When the channel is ready we create the interface
             */
            remote = new easyXDM.Interface(/** The channel configuration*/{}, /** The configuration */ {
                remote: {
                    alertMessage: {
                        isVoid: true
                    }
                },
                local: {
                    remoteApplicationMethod: {
                        method: doSomething(value1, value2){
                        // do somethigs with values

                        return "i'm return value from remote domain";
                        }
                    },
                    noOp: {
                        isVoid: true,
                        method: function(){
                            alert("Method not returning any data");
                        }
                    }
                }
            });
        }
    </script>
于 2012-06-09T21:53:09.440 回答
3

我想没有必要支持旧的浏览器,对吧?您可以在现代浏览器中使用window.postMessage来支持跨域通信。

于 2012-06-12T07:11:54.720 回答
2

你不会相信原因。在 main_page.html (abc.com) 上,您定义了两个 iframe,但您没有关闭它们(缺少 </iframe>)。现在有两种情况:

情况1:

iframe 是独立的,因此您的 main_page.html 代码应该是

<iframe src="http://xyz.com/visible_iframe.html" ...></iframe>
<iframe src="http://xyz.com/invisible_iframe.html" ...></iframe>

并且来自 invisible_iframe.html 的 javascript 调用应该是

parent.frames["visible_iframe"].Hi();

案例二:

iframe 是父子,所以 main_page.html 代码应该是

<iframe src="http://xyz.com/visible_iframe.html" ...></iframe>

visible_iframe.html 代码应该包括

<iframe src="http://xyz.com/invisible_iframe.html" ...></iframe>

并且来自 invisible_iframe.html 的 javascript 调用应该是

parent.Hi();

就这样。这是你的选择。

于 2012-06-10T21:37:57.983 回答
0

假设你的onlcick作品是你的输入(只是一个错字):从,你invisible_frame应该用.visible_frameHi()parent.frames['visible_frame'].Hi()

现在,parent.Hi()尝试访问Hi()位于abc.com' 页面(因为它是框架的parent),所以你被同源策略的硬棒戳了戳。

希望这有帮助并且有效;)

于 2012-06-07T09:52:31.907 回答
0

尝试创建这三个文件:

http://abc.com/main.html

<!DOCTYPE html>
<html>
<head>
    <script>
    function SendMsg(id) {
        window.frames.invisible.location = 'http://xyz.com/invisible.html#' + id;
    }
    </script>
</head>
<body>
    <input type="button" id="Test" value="Call iFrame" onclick="SendMsg(this.id);">
    <iframe src="http://xyz.com/visible.html" name="visible" height="250" width="500"></iframe>
    <iframe name="invisible" height="0" width="0" style="display:none;"></iframe>
</body>
</html>

http://xyz.com/visible.html

<!DOCTYPE html>
<html>
<head>
    <script>
    function Hi() {
        alert('Hi there!');
    }
    </script>
</head>
<body>
    <h1>Visible on xyz.com</h1>
</body>
</html>

http://xyz.com/invisible.html

<!DOCTYPE html>
<html>
<head>
    <script>
    var sActionText = "";
    function CheckForMessages() {
        if (location.hash != sActionText) {
            sActionText = location.hash;
            var sAction = "";
            var oSplitActionText = sActionText.split("#");
            sAction = oSplitActionText[1];
            if (sAction == "Test") {
                parent.frames.visible.Hi();
            }
        }
    }
    setInterval(CheckForMessages, 200); 
    </script>
</head>
<body>
    <h1>Invisible on xyz.com</h1>
</body>
</html>

请注意,框架结构是:

       main
       /  \
      /    \
visible    invisible

所以如果你需要从 访问visibleinvisible你需要去:

parent.frames.visible.Hi()

展望未来,我建议使用easyXDMjQuery postMessage 插件进行跨域通信,而不是重新发明轮子。

于 2012-06-12T14:32:31.250 回答
0

由于相同的来源策略,它不允许您访问该功能是有原因的。如果可以的话,它会导致真正的安全问题。

于 2012-06-12T17:09:17.290 回答
0

如果您不关心 IE7 支持,CORS(跨域资源共享)window.postMessage(参考:MDN 上的文档或另一个简单的 postMessage 示例)绝对值得研究;您还可postMessage以为较旧的 IE 浏览器使用 shim。

但是,我将坚持使用 iframe 解决方案,以更准确地回答您的问题。

不幸的是,您应该无法从跨域 iframe访问parent(范围)属性/方法。window但是,您可以window通过同一域上的 iframe 访问范围......在任何深度。因此,一种解决方案是利用垂直 iframe 隧道。

如果创建三个 html 文档:

顶级 - abc.com

<script>
    function sendMsg( id ) {
       frames["invisible_iframe"].location = "//xyz.com/mid_level.html#" + id;
    }

    window.ACTIONS = {
        Test: function() {
            alert("hello");
            // This action could also start an interaction with another
            // visible iframe by setting a hash, etc
        }
    };
</script>
<input type="button" id="Test" value="Call iFrame" onclick="sendMsg(this.id);">
<iframe name="invisible_iframe" height="0" width="0" frameborder="0" style="visibility:hidden;" src="//xyz.com/mid_level.html"></iframe>

中级 - xyz.com

<script>
    function sendMsg( id ) {
       frames["invisible_iframe"].location = "//abc.com/low_level.html#" + id;
    }

    var sActionText = "";
    function checkForMessages() {
       if(location.hash != sActionText) {
         sActionText = location.hash.replace(/^#/, "");
         location.hash = "";

         sendMsg(sActionText);
       }
    }

    setInterval(checkForMessages, 20); 
</script>
<iframe name="invisible_iframe" height="0" width="0" frameborder="0" style="visibility:hidden;" src="//abc.com/low_level.html"></iframe>

低级 - abc.com

<script>
    function runActionInTop( action ) {
        try {
            window.top.ACTIONS[action]();
        } catch ( err ) {
            // Bad action!
        }
    }

    var sActionText = "";
    function checkForMessages() {
       if(location.hash != sActionText) {
         sActionText = location.hash.replace(/^#/, "");
         location.hash = "";

         runActionInTop(sActionText);
       }
    }

    setInterval(checkForMessages, 20); 
</script>

这三个 iframe 可以通信 - 第一个和第三个可以直接通信,第二个可以间接通信(在本例中通过哈希)。

我知道这与您最初尝试使用“可见 iframe”显示的内容有所不同。我想重点说明跨站点通信所需的分层。如果您仍然需要可见的 iframe,您可以在顶层添加通信挂钩,并将附加信息(在哈希中)从中间帧发送回最低帧(最低帧可以直接将信息传递回顶部页)。

是的,我知道这很复杂——这就是 CORS 存在的原因之一postMessage:)

我还更改了我认为值得指出的原始代码的一些小方面:

  • 使用//而不是http://意味着该系统将在 http 或 https 上运行。
  • 一些浏览器(我相信我是在 Safari 3 中第一次遇到这个问题)实际上可能不会使用display:none;加载 iframe。设置frameborder属性visibility:hidden并将阻止 iframe 可见。
  • about:blank如果您未src在标记中指定 iframe 属性,某些浏览器将加载(或类似页面)。在 https 页面上,这可能会导致在 IE 中引发不安全的内容警告。
于 2012-06-13T15:31:00.763 回答
0

另一种选择,代理。

在服务器端抓取外部文件并将其回显。现在它在您的域上,所以没有问题。

例如在php

<?php
  $external-site = file_get_contents('http://xyz.com/');
  echo $external-site;
?>
于 2012-06-15T02:13:18.497 回答
0

我想我会删除任何 iframe 并将 jsonp 与库一起使用。1. 没有跨域错误 2. 您可以将数据作为对象获取(易于使用它们 3. 您甚至可以获得您可以解析的 JS 代码。4. 甚至可以移动设备(iframe 是上世纪的解决方案.. .) 但是,也许您出于任何原因无法删除 iframe,所以我建议使用现成的库解决方案。祝好运

于 2012-06-16T08:26:10.153 回答