今天我们来探讨一下在ClickOnce部署中如何严格控制应用程序的权限。
演示应用
为了在下文中能更好地演示,我们先要做一个测试项目。也为了显得简单易懂,我使用最常用且最常见的WinForm项目,这是地球上最丰富的物种。
咱们做一个MD5的计算程序,这个应该够简单了吧?程序的功能是浏览打开一个文件,然后计算它的MD5,最后以字符串的形式输出。
界面大致这样:
其中用来输入文件名的文本框叫txtInput,用来显示计算结果的文本框叫txtOutput,为了方便输入文件名,我们在输入文本框的右侧放一个按钮,点击后通过OpenFileDialog来浏览文件。
然后处理的代码如下:
using System;
……
using System.Windows.Forms;
using System.Security.Cryptography;
using System.IO;
namespace md5ComputeApp
{
public partial class Form1 : Form
{
……
private void button1_Click(object sender, EventArgs e)
{
if (File.Exists(this.txtInput.Text) == false)
{
MessageBox.Show("靠,文件不存在,计算条毛啊。"); return;
}
MD5 md5 = new MD5CryptoServiceProvider();
// 打开文件流
FileStream fs = File.OpenRead(txtInput.Text);
// 开始计算
byte[] dataBuffer = md5.ComputeHash(fs);
// 关闭流
fs.Close();
fs.Dispose();
fs = null;
// 转换为字符串
StringBuilder bd = new StringBuilder();
foreach (byte b in dataBuffer)
{
bd.Append(b.ToString("x2"));
}
// 显示结果
this.txtOutput.Text = bd.ToString();
}
private void btnBrowser_Click(object sender, EventArgs e)
{
if (this.openFileDialog1.ShowDialog().Equals(DialogResult.OK))
{
this.txtInput.Text = this.openFileDialog1.FileName;
}
}
}
}
好,运行一下,试试是否达到预期效果。如下图:
OK,相当Nice,基本符合要求了。接下来我们就要对其进行部署了。
完全信任权限
部署我们肯定都会,而且前面我们也介绍过了。不过这一次我们重点是设置安全权限。为了保护我们的程序不被别有用心的人拿来干坏事,有时候我们没有必要让应用程序具有过高的权限。
在项目属性窗口中,切换到"安全性"选项卡,勾选"启用ClickOnce安全设置"复选框,然后下面的几个单选项就变为可用状态。我们保持选中第一个,即"这是完全可信的应用程序"。最后保存。
现在我们部署这个应用程序,相信大家都会了,就是切换到"发布"选项卡来完成,和前面所讲的方法一样。
安装后我们看到程序正常运行了。
部分可信任权限
所谓部分可信任,就是相对我们前面的完全信任权限而言的,即应用程序不具备所有权限,比如,可能只允许应用程序访问D盘下的文件,或者说不允许应用程序进行反射操作等。
要知道有哪些权限可以自定义也很简单,打开"对象浏览器"窗口找到System.Security.IPermission接口,然后我们查看实现它的类,以及这些类的子类,一层一层往下翻,那些命名为XXXPermission的类就是对应的权限了。除此之外,我们还会看到一些Attribute,如FileDialogPermissionAttribute,即它们的命名规律是XXXPermissionAttribute,我们只要找到这些规律,后面在配置权限时就好办了。
现在,我们先不管权限如何配置,因为VS默认会为我们定义两个权限集。依旧,打开项目属性窗口,同样切换到"安全性"选项卡,这一次我们选择"部分信任的应用程序",如下图所示:
在"要从中安装应用程序"下面的下拉列表框,为我们预定义了两种权限集,一种是内部Intranet,一种是广域Internet。我们可以根据具体情况来选。
这里顺便告诉大家,这几个预定义的权限集在哪里。打开C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\PermissionSets目录,你会看到有三个XML文件,和上面安全性配置上的"完全信任",部分信任中的"Internet"、"Intranet"相对应。我这里是.NET 4.5.1,具体看你安装的版本。
FullTrust.xml——表示完全信任的权限配置。
Internet.xml——表示Internet上的应用的权限配置。
LocalIntranet.xml——本地Intranet的权限配置。
我们可以打开这些XML文件看看里面有些什么。
从中我们看到,每一项权限都是一个IPermission节点,这个IPermission是不是有点熟悉?对的,上面我们说过的一个接口,与权限配置有关的类都实现该接口。也就是说,自定义应用程序的权限,就是编写相应的XML节点。
从MSDN的参考文档(参考地址:http://msdn.microsoft.com/ZH-CN/library/vstudio/ws1c2fch(v=vs.110).aspx)我们知道,权限集的配置在trustInfo元素下的security/applicationRequestMinimum/PermissionSet节点下,层次结构我们可以参考文档。
好,我们来实战一下,这样讲还是觉得很抽象,再次打开项目属性,切换到"安全性"选项卡。选择部分信任选项,在"将要从中安装应用程序的区域"下面的下拉列表框中选择"自定义",这时候,右边的"编辑权限XML"按钮变为可用状态。如下图:
我们不妨单击这个按钮,来查看XML文件,结合XML文件上的注释和文档说明,基础较好的朋友估计已经明白怎么用了。
现在呢,不对XML文件进行任何修改,然后我们发布并安装应用程序。结果我们发现,应用程序没运行,手动到"开始"组中启动它也没有运行,这是很多朋友都遇到的问题,ClickOnce应用安装后不能运行。其实问题就出在权限上。刚才我们使用自定义权限集来发布应用程序,而我们又没有配置任何权限,就等于应用程序不具有任何权限,当然没法运行。
回到项目属性窗口的"安全性"选项卡,点击"编辑权限XML"按钮。
这时候就打开应用程序清单文件,我们找到trustinfo节点。找到PermissionSet节。如下图:
注意PermissionSet节的ID要与defaultAssemblyRequest.permissionSetReference的值一致,这里已经为我们生成了Custom。
我们要做的就是在PermissionSet下添加IPermission元素,这个元素怎么写呢?看文档。Version属性为1,我们只需设置class即可,需要赋给应用程序什么权限,就写上类的完整标识符,包括程序集和版本,公钥等。
前面我为什么要介绍在对象浏览器中查看IPermission派生的类,就是为这里打下伏笔的。
IPermission元素所属性并不只是class、version这几个,它可以和对应的类的属性进行合并,即XXXPermission和XXXPermissionAttribute的属性可以进行合并,如FileIOPermission类有个AllFiles属性,FileIOPermissionAttribute类有个Read属性,我们就可以把它们合起来用,即:
<IPermission class=" System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" AllFiles="Read", Read="c:\" />
下面我们来为本示例的应用程序来配置一下权限,让它可以顺利地运行起来。
首先,我们在PermissionSet元素下添加以下两个权限:
<IPermission class="System.Security.Permissions.UIPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
Flags="Execution, BindingRedirects"/>
这些类绝大部分位于mscorlib程序集内,我们进行复制就行了,至于说如何查看publickey等参数,我可以向大家说一种方法。打开VS的命令行工具窗口,当然,打开cmd也行,只要路径定义对就行了。输入以下命令:
Gacutil /l mscorlib >>d:\123.txt
执行后就可以在d盘下的123.txt文件中输出结果,我们打开那个文本文件就可以把结果进行复制了。
后面的processorArchitecture是指CPU架构,这个不用复制。
哦,还有,如果大家觉得去打开命令行工具麻烦,可以把常用的工具都集成到VS中,方法是在VS窗口中执行菜单【工具】-【外部工具】,随后弹出窗口,然后我们单击"添加"按钮。
标题输入如"开发人员命令行工具",随便填,你自己知道什么意思就行。
命令输入%ComSpec%,即cmd,记住首尾两个百分号不要掉了。
参数输入/k ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat""。注意是双重引号,你没看错。如下图:
然后确定保存,以后你只需在VS中执行【工具】菜单,就会看到【VS命令行工具】项了,这样来打开就方便多了。
好了,说了很多废话,还是回到我们的正题,上面我们为应用程序清单定义了两个权限:
UIPermission:这是必须的,没有它应用程序界面就无法显示,你看它的名字就知道它的用途了。
SecurityPermission:它的Flags属性可以是多个枚举值合并,在XML配置中我们可以用逗号分隔多个值,注意是英文的逗号,不解释。Execution是少不的,没有它就没法运行应用程序,BindingRedirects还是加上好一点,有时候读取配置文件时要用到。
好,现在我们再次发布并安装应用程序,兴奋吧,程序终于运行起来了。如下图:
不过,当我们单击右边的按钮浏览文件时,就发生错误了。
这个错误也是没有权限造成的,回到项目,在清单文件上,我们继续添加可以操作文件对话框的权限。
再添加以下两个权限:
<IPermission class="System.Security.Permissions.FileDialogPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
Access="Open"
Unrestricted="true"/>
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
All="Read"
Unrestricted="true"/>
FileDialogPermission的Access为Open表示允许使用打开文件对话框,但光这个还不够,因为我们在计算MD5时要读取文件的内容,所以,还要加一个FileIOPermission权限,All为Read表示程序对文件只有读的权限,不能写,对于我们的MD5计算器来说够了。Unrestricted设置为true把权限完全赋给程序以免应用程序在运行时申请权限失败。
我们再次发布并安装应用程序,这一次看看能不能完成程序的功能。
这回正常了,高兴一下吧。
经过本节的演示,我们发现,完全自定义权限是相当麻烦的一件事,一般情况下我们不需要这样做,可以优先考虑不使用安全设置,或者使用预定义的权限。除非你确实需要严格控制,才会用自定义权限的方法。本节的演示也好让大家熟悉一下ClickOnce部署中分配权限的方法。