不管安全策略多么重要,但不到实施的时候,大部分时间部是闲置的。CLR自身总是隐式地实施安全策略,然向,在绝大多数情况下,安全策略都是由希望保护安全资源的受信的程序库显式地实施的。可以通过要求所有调用方都必须被授予一个特定的权限或权限集,来执行安全策略。为此,IPermission
接口和FermissionSet类都支持Demand方法,以允许显式实施策略。
Demand方法触发一个堆栈遍历,其中每一个方法的权限都将受到检查。CLR将方法所属程序集的证据传入安全策略,从而评估每一个方法的权限。假如堆栈上至少有一个方法不具备所要求的权限,Demand方法都将抛出一个System.Security.SecurityException异常。假如堆栈上的所有方法都具有所要求的权限,Demand方法将通过不抛出该异常而指明这一点。
为了调用Demand方法,你首先需要一个指定了要求哪些权限的权限或权限集对象。考察下面的C#方法:
public sealed class Utils{
public static IPAddress LookupHost(string host){
return Dns.GetHostByName(host).AddressList[0];
}
}
Dns.GetHosByName方法通过在内部调用Demand与法,要求所有调用方都必须具有System.Net.DnsPermission权限:
using System.Security.Permission;
namespace System.Net{
public sealed class Dns{
public static IPHostEntry GetHostByName(string host){
//实施安全策略
DnsPermission perm=new DnsPermission(PermissionState.Unrestricted);
perm.Demand();
//如果到了这步,DNS查找就会被策略允许,实际的工作就是这样的
//为了清晰起见,省略其余的部分
}
}
}
注意,GetHostByName方法只在方法的起始处关心安全问题。假如策略禁止DNS查找,Demand方法将会防止执行方法的剩余部分。当然,假如Gtils.LookupHost方法要求这一点,则应该进行显式地异常处理:
using System.Security;
public sealed class Utils{
public static IPAddress LookupHost(string host)
{
try{
return Dns.GetHostByName(host).AddressList[0];
}
catch(SecurityException){
retrun IPAddress.Loopback;
}
}
}
对于关心异常处理的应用来说,不满足要求的权限类型,可以通过安全异常对象的SecurityException.Permission.Type属性访问到。
如图9.7所示,Demand方法要求堆栈上的每一个方法都必须具有所要求的最小的权限。“最小的权限”由权限类型的IeSubsetOf方法的实现定义。当一个线程开始使用CLR时,堆栈项部(top-of-stack)权限集就被建立。对于由基于CLR的程序显式创建的线程来说。这个权限集就是生产线程(spawning thread)的调用堆栈上各个权限的交集。类似的,当一个线程池里的线程开始服务一个工作请求时,来自生产线程(spawningthread)的一个快照,将被用于设置堆栈顶部权限。在这两种情况下,都可以防止辅助线程(secondary thread)执行生产线程(spawning thread)无法合法执行的操作。对于其他线程(包括基于CLR的应用程序的“主”线程)来说,CLR根据AppDomain被创建时提供的证据,计算堆栈顶部权限。AppDomain.CreateDomain方法接收一个类型为System.Security.Policy.Evidence的参数用于这个目的。

先前展示的来自System.NeL.Dns类的示例,是一个强制式安全要求(imperative security demand)的例子。之所以称之为强制式,是因为它执行了一个显式的程序声明。CLR还支持基于特性(
Attribute)的声明式安全要求(declarative security demand)。对于每种权限类型来说,CLR都定义了权限相关的定制特性,它派生于System.Security.Permissions.CodeAcessSecurityAttribute。这些权限相关的特性,全部强制要求接收一个类型为system.Security.Permission.SecurityAction的构造器参数:
namespace System.Security.Permissions{
public enum SecurityAction{
Demand=1,
Assert,
Deny,
PermitOnly,
LinkDemand,
InheritanceDemand,
RequestMinimum,
RequestOptional,
RequestRefuse,
}
}
这部分讨论惟一考虑的SecurityAction是Demand,考虑下面的强制式要求:
using System.Security.Permissions;
namespace System.Net{
public sealed class Dns{
public static IphostEntry GetHostByName(string host){
DnsPermission perm=new DnsPermission(PermissionState.Unrestricted);
perm.Demand();
//如果我们到了这里,该要求就被满足了
}
}
}
这个安全行为允许你重写强制式要求,如下所示:
using System.Security.Permissions;
namespace System.Net{
public sealed class Dns{
[DnsPermission(SecurityAction.Demand,Unrestricted=true)]
public static IPHostEntry GetHostByName(string host){
//如果我们到了这里,该要求就被满足了
}
}
}
后一个示例的优点在于,你可以通过查看类型的元数据而精确地决定代码的安全需求。声明式安全要求的缺点在于,它们不支持需要动态信息(例如,文件路径)的权限,因为这些动态信息在编译时是不知的。诸如DnsPermission这样的安全特性,既可以应用于个体方法之上,也可以针对整个类型,后一种情况实际上是将这个特性应用于这个类型的所有方法之上。1