无法在跨域环境中访问JS功能

时间:2012-06-07 08:25:16

标签: javascript iframe

为了解决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>

我在visible_iframe.html中使用隐藏的iFrame,因为我不希望更改visible_iframe.html的网址。

现在我希望点击main_page.html上的按钮时,我会收到提醒信息。但这不会发生。在Firefox中它说 - 权限被拒绝访问属性'嗨'

奇怪的是,当我将parent.Hi();放在CheckForMessages()函数之外时,可以访问Hi()函数并显示警告框。

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

9 个答案:

答案 0 :(得分:4)

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

  

easyXDM是一个Javascript库,可以让您作为开发人员使用   轻松解决同源的限制   策略反过来又很容易沟通和公开javascript   API跨域边界。

[这是最好且易于使用的API之一] 可用于Web应用程序之间的跨域通信。 easyXDM 易于使用,重量轻,灵活,编写高质量的代码等。我强烈认为如果你要继续跨域方案,那么你应该适应一个强大的跨域apis,如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>

答案 1 :(得分:3)

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

答案 2 :(得分:2)

你不会相信原因。在main_page.html(abc.com)上,您可以定义两个iframe,但不要关闭它们(缺少&lt; / iframe&gt;)。现在有两种情况:

案例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();

案例2:

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();

这就是全部。选择是你的。

答案 3 :(得分:0)

假设您的onlcick适用于您的输入(只是一个错字):来自invisible_frame,您应该visible_frame Hi() parent.frames['visible_frame'].Hi()

目前,parent.Hi()尝试访问位于Hi()页面上的abc.com(因为它是框架的parent),所以你被戳了戳同源政策的坚持。

希望这有用并且有效;)

答案 4 :(得分: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

因此,如果您需要从visible访问invisible,则需要:

parent.frames.visible.Hi()

继续前进,我建议使用easyXDMjQuery postMessage plugin进行跨域通信,而不是重新发明轮子。

答案 5 :(得分:0)

由于same origin policy,有一个原因是它不允许您访问该功能。如果可以,它会导致真正的安全问题。

答案 6 :(得分:0)

如果您不关心IE7支持,

CORS (Cross-Origin Resource Sharing)window.postMessage(参考:Docs on MDN或其他simple postMessage example)绝对值得调查;你也可以使用postMessage垫片用于较旧的IE浏览器。

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

很遗憾,您无法从跨域iframe访问parentwindow范围)属性/方法。不过,您可以通过同一域上的iframe访问window范围......任意深度。因此,一种解决方案是利用垂直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不可见。
  • 如果您未在标记中指定iframe about:blank属性,某些浏览器将加载src(或类似页面)。在https页面上,这可能会导致在IE中抛出不安全的内容警告。

答案 7 :(得分:0)

另一种选择,代理。

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

例如在php

<?php
  $external-site = file_get_contents('http://xyz.com/');
  echo $external-site;
?>

答案 8 :(得分:0)

我想我会删除任何iframe并使用jsonp可能与库。 1.没有跨域错误 2.您可以将数据作为对象获取(易于使用它们 你甚至可以获得你可以解析的JS代码。 4.甚至可以移动就绪(iframe是上个世纪的解决方案......) 但是,也许你可能因为任何原因无法取走iframe,所以我建议使用现成的库解决方案。 古德勒克