通过脚本访问不同域中的内容

Adobe AIR 1.0 和更高版本

AIR 应用程序在安装时会被授予特殊权限。不要将相同权限泄露给其他内容(包括不属于应用程序的远程文件和本地文件),这一点很重要。

关于 AIR 沙箱桥

通常,一个域中的内容无法调用其他域中的脚本。为了保护 AIR 应用程序不会意外泄露获得权限的信息或控制,对 应用程序 安全沙箱中的内容(随应用程序一起安装的内容)施加了以下限制:

  • 无法通过调用 Security.allowDomain() 方法允许应用程序安全沙箱中的代码访问其他沙箱。从应用程序安全沙箱中调用此方法将引发错误。

  • 禁止通过设置 LoaderContext.securityDomain LoaderContext.applicationDomain 属性将非应用程序内容导入应用程序沙箱。

但是仍存在这样一些情况:主 AIR 应用程序要求远程域中的内容对主 AIR 应用程序中的脚本具有受控访问权限,反之亦然。为此,运行时提供了 沙箱桥 机制,沙箱桥充当两个沙箱之间的通道。沙箱桥可以在远程安全沙箱和应用程序安全沙箱之间提供显式交互。

沙箱桥公开了以下两个对象,已加载和要加载的脚本都可以访问这两个对象:

  • parentSandboxBridge 对象允许要加载的内容将属性和函数公开给已加载的内容中的脚本。

  • childSandboxBridge 对象允许已加载的内容将属性和函数公开给要加载的内容中的脚本。

通过沙箱桥公开的对象按值而不是按引用进行传递。所有数据都会序列化。这意味着由桥的一端公开的对象无法由另一端设置,并且公开的对象为无类型对象。此外,只能公开简单对象和函数;不能公开复杂对象。

如果子级内容试图设置 parentSandboxBridge 对象的一个对象,则运行时会引发 SecurityError 异常。同样,如果父级内容试图设置 childSandboxBridge 对象的一个对象,则运行时也会引发 SecurityError 异常。

沙箱桥示例 (SWF)

假设 AIR 音乐商店应用程序需要允许远程 SWF 文件广播专辑价格,但不需要远程 SWF 文件透露该价格是否为销售价格。为此,StoreAPI 类提供了一种获取价格的方法,但屏蔽了销售价格。随后会将此 StoreAPI 类的实例分配给 Loader 对象(该对象加载远程 SWF)的 LoaderInfo 对象的 parentSandboxBridge 属性。

以下是 AIR 音乐商店的代码:

<?xml version="1.0" encoding="utf-8"?> 
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" title="Music Store" creationComplete="initApp()"> 
    <mx:Script> 
        import flash.display.Loader; 
        import flash.net.URLRequest; 
     
        private var child:Loader; 
        private var isSale:Boolean = false; 
         
        private function initApp():void { 
            var request:URLRequest =  
                    new URLRequest("http://[www.yourdomain.com]/PriceQuoter.swf") 
 
            child = new Loader(); 
            child.contentLoaderInfo.parentSandboxBridge = new StoreAPI(this); 
            child.load(request); 
            container.addChild(child); 
        } 
        public function getRegularAlbumPrice():String { 
            return "$11.99"; 
        } 
        public function getSaleAlbumPrice():String { 
            return "$9.99"; 
        } 
        public function getAlbumPrice():String { 
            if(isSale) { 
                return getSaleAlbumPrice(); 
            } 
            else { 
                return getRegularAlbumPrice();     
            } 
        } 
    </mx:Script> 
    <mx:UIComponent id="container" /> 
</mx:WindowedApplication> 

StoreAPI 对象将调用主应用程序来检索常规专辑价格,但在调用 getSaleAlbumPrice() 方法时将返回“Not available”。以下代码定义了 StoreAPI 类:

public class StoreAPI 
{ 
    private static var musicStore:Object; 
     
    public function StoreAPI(musicStore:Object) 
    { 
        this.musicStore = musicStore; 
    } 
 
    public function getRegularAlbumPrice():String { 
        return musicStore.getRegularAlbumPrice(); 
    } 
     
    public function getSaleAlbumPrice():String { 
        return "Not available"; 
    } 
     
    public function getAlbumPrice():String { 
        return musicStore.getRegularAlbumPrice();     
    } 
}

以下代码表示的是 PriceQuoter SWF 文件示例,该文件报告了商店价格,但不能报告销售价格。

package 
{ 
    import flash.display.Sprite; 
    import flash.system.Security; 
    import flash.text.*; 
     
    public class PriceQuoter extends Sprite 
    { 
        private var storeRequester:Object; 
         
        public function PriceQuoter() { 
            trace("Initializing child SWF"); 
            trace("Child sandbox: " + Security.sandboxType); 
            storeRequester = loaderInfo.parentSandboxBridge; 
             
            var tf:TextField = new TextField(); 
            tf.autoSize = TextFieldAutoSize.LEFT; 
            addChild(tf); 
             
            tf.appendText("Store price of album is: " + storeRequester.getAlbumPrice()); 
            tf.appendText("\n"); 
            tf.appendText("Sale price of album is: " + storeRequester.getSaleAlbumPrice()); 
        } 
    } 
}

沙箱桥示例 (HTML)

在 HTML 内容中,会将 parentSandboxBridge childSandboxBridge 属性添加到子级文档的 JavaScript window 对象中。有关如何在 HTML 内容中设置桥函数的示例,请参阅 设置沙箱桥接口

限制 API 公开

公开沙箱桥时,公开限制沙箱桥滥用程度的高级别 API 非常重要。请注意,调用桥实施的内容可能会被破坏(例如,通过插入代码)。因此(举例来说),通过桥公开 readFile(path:String) 方法(读取任意文件的内容)易于受到滥用。最好公开未使用路径且读取特定文件的 readApplicationSetting() API。一旦应用程序部分受到损坏,语义方法越多,就越能限制应用程序产生的破坏。