.NET: 通过AppDomain级别控制安全策略的例子
这是在课堂上讲的一个小范例。场景是:
1. 我们有一个主程序,它公开了一套API,允许其他开发人员为它设计插件
2. 因为插件不是我们设计的,所以我们需要确保这些插件不会恶意地伤害到用意。为此,我们希望将插件的运行权限降低。
解决方案就是,我们单独用一个AppDomain来运行这些插件,并且在该AppDomain级别单独设置安全策略。
1. API
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace API { public interface IPlugin { void Run(); } }
2. Plugin
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace PluginSample { public class Plug:API.IPlugin { #region IPlugin 成员 void API.IPlugin.Run() { MainForm form = new MainForm(); form.ShowDialog(); } #endregion } }
Plugin中有一个窗体,里面有一个按钮,执行访问注册表的操作
using System; using System.Windows.Forms; using Microsoft.Win32; namespace PluginSample { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { RegistryKey key = Registry.CurrentUser.CreateSubKey("Plugins"); key.SetValue("Version", "1.0.0"); key.SetValue("Update", DateTime.Now.ToString()); MessageBox.Show("操作已经完成"); } } }
3. 宿主程序
为了实现跨应用程序域的加载,我们设计了一个AssemblyLoader类型
using System; using System.Linq; using System.Reflection; namespace Host_WinForms { [Serializable] class AssemblyLoader:MarshalByRefObject //必须继承MarshalByRefObject,否则无法实现AppDomain级别的安全控制 { public string[] GetPluginTypeNames(string path) { Assembly ass = Assembly.LoadFile(path); var query = from type in ass.GetTypes() where type.GetInterface(typeof(API.IPlugin).FullName) != null select type.FullName; return query.ToArray(); } public void RunPlugin(string path, string typeName) { Assembly ass = Assembly.LoadFile(path); API.IPlugin plugin = (API.IPlugin)ass.CreateInstance(typeName); plugin.Run(); } } }
宿主程序有一个MainForm
using System; using System.Windows.Forms; using System.IO; using System.Security.Permissions; using System.Security.Policy; using System.Security; namespace Host_WinForms { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void Log(string message) { richTextBox1.AppendText(DateTime.Now.ToString().PadRight(25)); richTextBox1.AppendText(message); richTextBox1.AppendText(Environment.NewLine); } private void MainForm_Load(object sender, EventArgs e) { Log("主程序启动"); Log("主程序域中的程序集:"); foreach (var item in AppDomain.CurrentDomain.GetAssemblies()) { Log(item.FullName); } //加载所有的插件(遍历当前目录下面的dll),动态创建菜单项目 AppDomain plugindomain = AppDomain.CreateDomain("PluginDomain"); AssemblyLoader loader = (AssemblyLoader)plugindomain.CreateInstanceFromAndUnwrap("Host_WinForms.exe", typeof(AssemblyLoader).FullName); foreach (var item in Directory.GetFiles(Environment.CurrentDirectory, "*.dll")) { foreach (var type in loader.GetPluginTypeNames(item)) { ToolStripMenuItem menuitem = new ToolStripMenuItem(type); menuitem.Tag = item; menuitem.Click += (o1, e1) => { //当点击了之后执行有关的插件 AppDomain plugindomaintemp = AppDomain.CreateDomain("PluginDomain"); PolicyLevel level = PolicyLevel.CreateAppDomainLevel(); PermissionSet ps = level.GetNamedPermissionSet("Internet"); ps.AddPermission(new FileIOPermission( FileIOPermissionAccess.AllAccess, Environment.CurrentDirectory)); level.RootCodeGroup.PolicyStatement = new PolicyStatement(ps); plugindomaintemp.SetAppDomainPolicy(level); try { AssemblyLoader loadertemp = (AssemblyLoader)plugindomaintemp.CreateInstanceFromAndUnwrap( "Host_WinForms.exe", typeof(AssemblyLoader).FullName); ToolStripMenuItem temp = (ToolStripMenuItem)o1; loadertemp.RunPlugin(temp.Tag.ToString(), temp.Text); } catch (Exception ex) { MessageBox.Show(string.Format("运行插件发生如下错误:{0}", ex.Message)); } finally { AppDomain.Unload(plugindomaintemp); } }; AllPluginMenu.DropDownItems.Add(menuitem); } } AppDomain.Unload(plugindomain); Log("主程序域中的程序集:"); foreach (var item in AppDomain.CurrentDomain.GetAssemblies()) { Log(item.FullName); } } } }
4. 运行效果