System.Security.Permissions中的PrincipalPermission类是.NET中的一个比较特殊的权限类,它是目前唯一一个继承IPermission接口但是不继承CodeAccessPermission类的权限。
原因是这个权限是用来检查抽象用户权限(.NET中的IPrincipal接口),因此它和代码访问权限(CodeAccessPermission)这种基于程序集安全策略证据的权限管制没有关系,所以它成为唯一一个目前不继承CodeAccessPermission的权限类。当然它和CodeAccessPermission的本质最大区别是后者继承了IStackWalk接口(System.Security命名空间内),PrincipalPermission没有。这样的话,PrincipalPermission只有Demand方法,而IStackWalk接口除了有Demand方法,还有Assert,Deny和PermitOnly方法(分别用来断言,拒绝和只允许某个权限)。我们先通过类的继承关系图来看:
(IPermission接口)
(IStackWalk接口)
另外IStackWalk的继承类除了有CodeAccessPermission类代表代码访问权限基类,还有PermissionSet类代表一组权限。
代码访问权限类的Demand方法需要检查整个函数调用栈,确保所有函数栈中的程序都必须拥有所需权限(或者权限集合PermissionSet),一旦有一个不通过,SecurityException异常会被抛出。而PrincipalPermission这个用户权限是不需要这样做的,除非同一个函数调用栈包含不同用户级别的代码权限,显然这是不可能的。因此PrincipalPermission中的Demand拥有自己的检查机制,那就是检查当前线程的用户权限:Thread.CurrentPrincipal属性(MSDN的人工翻译是:获取或设置线程的当前负责人(对基于角色的安全性而言))。
至于Thread.CurrentPrincipal,默认是GenericPrincipal,原因是当前应用程序域的用户对象策略默认是给予PrincipalPolicy.UnauthenticatedPrincipal(这个可以在AppDomain.SetPrincipalPolicy方法中设置)。所以这个GenericPrincipal代表一个一般用户体,它的GenericPrincipal构造函数中的角色(字符串数组)是一个空字符串,而Identity属性则是一个GenericIdentity对象,标示名称是空字符串(Name属性),所以IsAuthenticated返回false。
//using System.Principal
IPrincipal principal = Thread.CurrentPrincipal;
Console.WriteLine(principal.GetType());
Console.WriteLine(principal.Identity.GetType());
输出:
System.Security.Principal.GenericPrincipal
System.Security.Principal.GenericIdentity
PrincipalPermission是用来验证当前用户信息的,显然默认的GenericPrincipal没有提供任何有用信息,我们之后会来构造一些更有用的。首先了解一下PrincipalPermission的创建。PrincipalPermission的构造函数:
class PrincipalPermission: IPermission
构造函数(string name, string role, bool isAuthenticated = true);
//name:验证用户名称,用null同意任何名称
//对应IIdentity.Name属性
//role:验证用户权限,用null同意任何权限
//对应IPrincipal.IsInRole方法
//isAuthenticated: 是否检查用户已验证(默认true)
//对应IIdentity.IsAuthenticated属性
(PrincipalPermission还有一个.NET权限都有的构造函数(参数是PermissionState枚举),但是在PrincipalPermission中没有多大用处,你可以在MSDN中查看其信息:http://msdn.microsoft.com/en-us/library/230thh38)
那么自定义一个IPrincipal接着就可以使用PrincipalPermission了:
try
{
//using System.Security;
//using System.Security.Permissions;
//using System.Security.Principal;
var permission = new PrincipalPermission("Mgen", null);
//将当前线程的IPrincipal设置成自定义的GenericPrincipal
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("Mgen"), new string[] { "Programmer" });
permission.Demand();
Console.WriteLine("成功");
}
catch (SecurityException)
{
Console.WriteLine("失败");
}
结果程序输出“成功”,因为用户名称符合当前线程IPrincipal.Identity.Name属性,而PrincipalPermission的角色验证是null会同意任何用户权限(角色)信息,比如上例中的”Programmer”。
上面仅仅是个示例,在更多情况下,PrincipalPermission用来验证当前Windows账户信息(WindowsPrincipal和WindowsIdentity),而不是GenericPrincipal和GenericIdentity。同样我们必须设置当前线程的用户信息,可以这样做:
//using System.Security.Principal;
Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
不过更自然的做法是用上面提到过的应用程序域的SetPrincipalPolicy方法:
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
好了,这样在当前应用程序域的线程或者进入当前应用程序域的CurrentPrincipal为null的线程的CurrentPrincipal都会自动成当前Windows账户信息Principal。
下面就可以判断WindowsPrincipal的信息,比如判断用户是否是管理员账户:
(注意在Windows Vista之后的系统,Windows用户账户控制打开的情况下,管理员账户并不会默认有管理员权限,你需要手动已管理员身份启动程序并同意用户账户控制提示才可以对程序赋予管理员权限)
try
{
//设置当前应用程序域的IPrincipal为WindowsPrincipal
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
//执行PrincipalPermission判断,匹配任何用户名称和管理员账户类型
new PrincipalPermission(null, "Administrators").Demand();
Console.WriteLine("是管理员");
}
catch (SecurityException)
{
Console.WriteLine("不是管理员");
}
Console.ReadKey(true);
本文版权归作者所有,欢迎以网址(链接)的方式转载,不欢迎复制文章内容的方式转载,其一是为了在搜索引擎中去掉重复文章内容,其二复制后的文章往往没有提供本博客的页面格式和链接,造成文章可读性很差。望有素质人自觉遵守上述建议。
如果一定要以复制文章内容的方式转载,必须在文章开头标明作者信息和原文章链接地址。否则保留追究法律责任的权利。