.net安全编程 阅读笔记(三)

基于角色的安全性

当用户访问一个计算机系统时,他们必须首先证明自己的身份,这个过程被称作验证,验证要求用户提供一组能够唯一标识自己身份的凭据,这通常是一个名称和密码,当然也有可能是一个物理存在的令牌,如密码卡,以及其他可以标识用户身份的东西。之后,计算机系统通过验证其身份来决定用户是否可以访问系统。并通过授权来决定用户具有的权限。这是典型的基于角色的安全性验证。而在.NET中,.NET提供了一个总目标的RBS(基于角色的安全性)抽象,可以结合各种验证和授权机制来使用该抽象,在.NET RBS中,标识代表了用户,通常情况下代表了当前登录的Windows用户,当然也有例外。
角色的主体包含了标识和标识属于的角色,如果标识代表了一个Windows用户帐户,其角色就能够识别用户属于的Windows组,因为主体压缩了表示和用户角色两部分,所以主体是主要参考点,.NET 运行库基于角色的安全性决定都是以该主要参考点为基础的。

.NET RBS不仅仅是Windows用户安全在.NET运行库的扩展,.NET RBS和Windows用户帐户系统都是完全独立的安全机制,它们能同时操作并服务于不同的目的。Windows的安全性通过控制用户访问重要操作系统的操作和资源来保护整个系统的稳定性,每个Windows进程和线程都具有一个相关的访问令牌(Token),该令牌代表了用户,而进程或线程就是基于这些用户的身份而运行的。访问令牌是一个操作系统对象,它含有用户权限和特权的信息,还包含了用户属于的组,在Windows允许代码执行受保护的操作或访问受限资源之前,它会评估线程的访问令牌来保证该线程含有执行操作的必要权限,无论一项操作是否被托管的.NET应用程序或本地代码启动,Windows都会强制执行安全性。.NET RBS 是在应用程序级别上的操作,它提供了一种合适的机制来控制访问基于用户标识和角色的功能,例如,也许只允许用户(某些角色的成员)调用一种重要的方法或者根据当前用户的角色来可视化不同的菜单项,运行.NET代码的每个线程都有一个相关的主体,该主体与Windows访问令牌的目的是类似的,它允许.NET决定运行线程代表的用户是否是指定角色的成员,尽管线程的主体和访问令牌可能都代表同一个用户,但他们是独立的对象,并且他们之间没有固定的关系,.NET RBS在使用时完全是可选的,应该负责决定哪些是被.NET RBS保护还必须表示出代码的要求。

编程基于角色的安全性

System.Security.Principal  命名空间含有Iidentity和Iprincipal接口来代表标识和主体,.NET类库含有四种具体的RBS实现,他们都使用Iidentity和Iprincipal接口:
Forms:提供一种基于角色的验证机制在ASP.NET应用程序中使用,Forms验证只提供Iidentity实现。
Generic:提供一种通用的基于角色的安全性实现,它是独立于任何指定验证和授权机制的。
Passport:提供一种基于角色的验证机制,它依赖于Microsoft Passport .NET 基于Web的服务来验证用户。
Windows:提供一种基于角色的安全性实现,它是以Windows用户帐户系统定义的用户为基础的。
下面是Iidentity和Iprincipal接口的成员
Iidentity interface:
AuthenticationType:一种属性,它返回了一个指定了验证类型的字符串,使用该验证来识别Iidentity对象代表的用户。
IsAuthenticated:一种属性,如果验证了Iidentity代表的用户,它返回“true”,否则返回“false”.
Name:一种属性,它返回一个含有Iidentity对象代表的用户名称的字符串。
IPrincipal Interface:
Identity: 一种属性,它返回一个Iprincipal对象包含的Iidentity对象。
IsInRole:一种方法,如果Iprincipal包含的Iidentity是指定名称的角色成员,它就返回”true“,否则返回”false“

在.NET运行库执行的每个线程都具有一个与其有关的Iprincipal对象,该线程的Iprincipal对象代表了一种用户,而运行的线程代表了该用户的利益。其Iprincipal对象还允许运行库为其基于用户标识和角色的线程做出基于角色的安全性决定。当然,许多应用程序并不使用.NET的RBS功能,所以也就不能保护好其性能和资源,.NET不会自动分配Iprincipal对象到每个线程,如果想使用RBS,一旦有必要,则必须手动分配Iprincipal给以讹线程,或者通过配置运行库来自动创建一个Iprincipal。可以使用System.AppDomain.SetThreadPrincipal方法来制定Iprincipal对象,具体如下,运行库会自动分配该对象到应用程序运行域里运行的每个线程,每个应用程序只能调用SetThreadPrincipal方法一次,否则会引发异常。

 

Code

此外,还可以通过System.Threading.Thread.CurrentPrincipal属性来手动设置当前线程的Iprincipal,从而不用使用SetThreadPrincipal来为整个应用程序域设置一个默认的Iprincipal,CurrentPrincipal总是影响当前执行的线程的Iprincipal,也可以使用这个属性来获得当前的Iprincipal.
线程是在应用程序域的主体策略中运行,如果不使用AppDomain.SetThreadPrincipal或Thread.CurrentPrincipal来分配Iprincipal到一个Thrand,当代码试图获得CurrentPrincipal时,则由应用程序域的主体策略来决定发生什么情况,可以使用System.AppDomain.SetPrincipalPolicy方法来配置应用程序域的主题策略。策略参数是System.Security.Principal.PrincipalPolicy枚举的一个成员,主要包含如下:
NoPrincipal:不会创建主

体或标识对象,获得Thread.CurrentPrincipal属性将返回null,所有基于代码的安全要求都会失败。
UnauthenticatedPrincipal:运行库创建了一个含有GenericIdentity的GenericPrincipal,设置其Name和AUthenticationType属性为空字符串,设置其IsAuthenticated属性为false,GenericIdentity不是任何角色的成员。
WindowsPrincipal:运行库创建了一个WindowsPrincipal,其含有的WindowsIdentity是基于当前线程的Windows访问令牌的,WindowsPrincipal含有的角色是当前用户的Windows组,
所有应用程序的默认主体策略是UnauthenticatePrincipal,它创建一个空的Iprincipal,这对做出基于角色的安全性决定并不起作用。
下面的代码示范了,利用CurrentPrincipal属性,为主体赋值,使其与WindowsIdentity一致。

 

Code

注意,应用这种方法,只会改变当前线程。

编程Windows基于角色的安全性的实现

System.Security.Principal命名空间的WindowsIdentity和WindowsPrincipal类提供了一个RBS实现,该实现允许将安全决定基于Windows用户帐户的标识和角色。WindowsIdentity类实现了Iidentity接口,并代表了Windows用户帐户,通过WindowsIdentity.Name 属性可以访问Windows用户的名称,该名称具有Domain\User格式 。WindowsPrincipal类实现Iprincipal,并含有用户的WindowsIdentity对象和包含用户的Windows组的名称,任意内置的Windows用户组的名称都具有BUILTIN\前缀,例如“BUILTIN\Administrator”或“BUILTIN\Users”,WindowsIdentity 除了实现了Iidentity接口定义的成员外还实现了其他一些成员,类似AuthenticationType,IsAnonymous,IsGuest等。WindowsPrincipal类中包含Identity属性,此属性将其包含的WindowsIdentity作为一个Iidentity对象返回,此外,WindowsPrincipal还包含方法IsInRole。这个方法有三种重载,包含int rid,string role和WindowsBuiltInRole role. 其中,rid参数指定了一个Windows角色标识(RID),RID是Windows组安全标识符的一个组件,它能提供方法以识别独立于语言本地化的组,string role 使用的是区分大小写的字符串,该字符串指定了测试对象的名称,这是Iprincipal定义的IsInRole的标准格式,最后是以WindowsBuiltInRole枚举作为参数,里面含有代表标准Windows组的值。部分通常使用的角色名称,RID和WindowsBuiltInRole成员如下:
名称                                        RID                          WindowsBuiltInRole
BUILTIN\Account Operators      0x224                         AccountOperator
BUILTIN\Administrator              0x220                         Administrator
BUILTIN\Backup Operators        0x227                         BackupOperator
BUILTIN\Guests                        0x222                        Guest
BUILTIN\Power Users               0x223                          PowerUser
BUILTIN\Print Operators           0x226                          PrintOperator
BUILTIN\Replicator                   0x228                          Replicator
BUILTIN\Server Operator          0x225                        SystemOperator
BUILTIN\Users                         0x221                         User

前面说过。默认情况下,如果不对当前主体进行设置,则.NET主体和Windows主体并不一致,如果想使当前主体代表当前活动的Windows用户,可以用如下SetPrincipalPolicy方法:AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);也可以手动创建WindowsIdentity和WindowsPrincipal对象,并将AppDomain.SetThreadPrincipal方法或Thread.CurrentPrincipal属性传递给他们。前者将会把主体分配给每个线程,后者,只分配给当前线程。如果需要代表不同主体的利益来是代码操作,也可以使用这两个方法来改变线程的主体,但是这并不会改变当前Windows访问令牌。

模拟Windows用户

有时候,我们需要模拟另一个Windows用户来进行一些操作,这个时候,我们需要用到一种叫做模拟的机制,这是由WindowsIdentity类提供的。首先,我们必须获得一个Windows访问令牌,该令牌代表了被模拟的用户,遗憾的是,当前的.NET Framework类库并没有能够托管访问Windows帐户数据库的类,因此必须调用非托管的advapi32.dll Win32库的LogonUser方法,获得访问令牌,LogonUser方法将用户的用户名和密码作为参数,并为用户提供对访问令牌的访问。一旦获得了访问令牌,就可以使用它来创建一个新的WindowsIdentity对象,并调用其Impersonate方法,Impersonate方法改变了当前线程的访问令牌,在调用Impersonate之后,便可将任意操作作为模拟用户来操作,Impersonate方法返回System.Security.Principal.WindowsImpersonateContext对象,该对象代表了优于模拟用户的Windows用户,如果要还原用户,则调用WindowsImpersonationContext.Undo方法。
以下示例示范了如何模拟一个名为“Bob”,密码为“treasure”的windows用户:

 

Code


 

posted on 2009-02-23 14:53  blue-boy  阅读(446)  评论(0编辑  收藏  举报

导航