• 如何通过AppDomain用特定的安全上下文加载外部程序集


    .NET的程序集实际上不是直接在进程(Process)中运行,而是在一个特殊的上下文环境(AppDomain)中。我们的程序在运行的时候,首先会由CLR动态创建一个或多个默认的AppDomain,然后我们在代码中还可以手工地创建自定义的AppDomain。

    有关程序域的说明,有兴趣的朋友可以参考下面的链接

    http://msdn.microsoft.com/zh-cn/library/system.appdomain(VS.80).aspx

    实际上它的三大好处是

    1. 安全性(不同程序域之间默认是无法通讯的)

    2. 稳定性(单一程序域的异常不会影响其他的)

    3. 便捷性(可以利用程序域的单独卸载,而不是整个程序卸载)

    那么什么时候需要用到这个技术呢?最典型的一个场景就是我们需要设计一个插件结构的程序。我们的主程序可以动态加载一个或者多个插件,这些插件当然可能是别人写的,那么我们对于这些插件所可能会做的事情是不可预期的。为了避免这些插件通过我们的主程序的执行而造成用户受到危害——例如插件可能会去更改注册表等等重要的信息——我们就可以把这些插件单独运行,而且让他们运行在相对比较低的安全上下文中。

    下面用一个例子说明,我们大致的原理是用一个单独的应用程序域运行所有的插件,在该应用程序域上设置的权限是Internet这个级别。

    fangz20910_1

    核心代码如下

    private AppDomain CreateAppDomain()
    {
        AppDomain result = AppDomain.CreateDomain("plugins");
        PolicyLevel policy = PolicyLevel.CreateAppDomainLevel();
        PermissionSet ps = policy.GetNamedPermissionSet("Internet");
    //这里我们取得Internet这个权限集的默认权限集合
        ps.AddPermission(new FileIOPermission(FileIOPermissionAccess.AllAccess, Application.StartupPath));
    //并且再为其增加文件读取的权限,因为它需要加载另外的程序集,所以需要有主程序根目录下面的权限
        policy.RootCodeGroup.PolicyStatement = new PolicyStatement(ps);
        result.SetAppDomainPolicy(policy);
        return result;
    }

    下面的代码是菜单响应事件

            void item_Click(object sender, EventArgs e)
            {
                ToolStripMenuItem item = (ToolStripMenuItem)sender;
                string tag = item.Tag.ToString();
    
                string fileName = tag.Split(';')[0];
                string typeName = tag.Split(';')[1];
               
                BindingFlags bindings = BindingFlags.CreateInstance | 
                    BindingFlags.Instance | BindingFlags.Public;
    
                AssemblyLoader loader = (AssemblyLoader)
                    pluginDomain.CreateInstanceFromAndUnwrap("Host.exe", 
                    typeof(AssemblyLoader).FullName, true, bindings, 
                    null, new object[] { fileName }, null, null, null);
                loader.RunPlugin(typeName);
            }

    上面的代码中的AssemblyLoader类相当于是一个桥梁,它帮助我们实例化插件并运行里面的入口方法

    using System;
    using System.Reflection;
    using PluginSample.Core; 
    
    namespace Host
    {
        [Serializable]
        public class AssemblyLoader:MarshalByRefObject
        {
            public AssemblyLoader() { } 
    
            public AssemblyLoader(string dllName)
            {
                if (_assembly == null)
                    _assembly = LoadAssembly(dllName);
            } 
    
            private Assembly _assembly=null;
            public Assembly LoadAssembly(string fileName) {
                return Assembly.LoadFile(fileName);
            } 
    
            public void RunPlugin(string type) {
                IPlugin plugin = (IPlugin)_assembly.CreateInstance(type);
                plugin.Main();
            } 
    
        }
    }
    
    下面我们来看看运行的效果。首先说说主程序:因为主程序是在本机运行,所以默认使用到的权限集是FullTrust,如下图
    image
    我们这个程序会自动地加载plugins目录下面的dll,然后创建菜单。(你这里可以看见两个菜单项,分别对应两个插件)
    我们接下去点击插件的菜单,这时候会调用上面贴出来的第二段代码,动态实例化插件,并将其放在单独的程序域中执行
    因为我们给程序域设置的权限集是Internet,所以你会看到下面的效果
    image
    注意,我们这里为了测试,故意在这个插件上用一个按钮去修改注册表,我们来看看会出现什么情况
    image
    很好,这样我们就实现了目的:可以让插件运行,但不允许他去修改注册表。
      
  • 相关阅读:
    C语言--存储类、链接和内存管理
    Linux终端使用技巧——个人总结
    mini2440应用例程学习(二)—— buttons
    ubuntu安装配置NFS服务方便mini2440挂载
    shell中常用I/O重定向命令格式说明
    Linux Bash内置命令大全详细介绍
    mini2440应用例程学习(一)—— led-player
    Shell编程练习(一)——ping一下
    < IOS开发 >使用CGContextRef绘制文字时的设置
    < Objective-C >使用kvc获取数组最大最小值
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/1286789.html
Copyright © 2020-2023  润新知