在基于 .NET 的应用程序中使用基于角色的安全设置
.NET Framework 在
System.Security.Principal 命名空间中提供了基于角色的安全设置实现,您可以用此实现来授权应用程序。要在 .NET Framework 中使用应用程序授权,请创建
IIdentity 和
IPrincipal 对象来表示用户。
IIdentity 封装的是一个通过验证的用户,
IPrincipal 则是用户标识和用户角色的组合。
图 4 显示了
IIdentity 和
IPrincipal 对象之间的关系。
图 4:IIdentity 和 IPrincipal 对象之间的关系请注意图 4 中的以下几点:
- IIdentity 对象是实现 IIdentity 类的实例。IIdentity 对象表示特定的用户。
- IIdentity 接口具有 Name、IsAuthenticated 和 AuthenticationType 属性。实现 IIdentity 的类通常还包含有特定用途的其他私有成员,例如,WindowsIdentity 类封装了正运行代码的用户的帐户令牌。
- IPrincipal 对象是实现 IPrincipal 的类的实例。IPrincipal 对象是代表用户的 IIdentity 及其具有的角色的组合。这样就可以实现单独的身份验证和授权。
- IPrincipal 对象使用 IsInRole 方法执行授权,并通过 Identity属性提供对 IIdentity 对象的访问。
使用标识
.NET Framework 提供了四种实现
IIdentity 接口的类:
- WindowsIdentity
- GenericIdentity
- PassportIdentity
- FormsIdentity
每种类都允许您使用不同种类的用户标识。要访问使用 Windows 身份验证的应用程序的当前
WindowsIdentity 对象,可以使用
WindowsIdentity 类的静态
GetCurrent 方法,如以下代码所示:
您还可以通过在自己定义的类中实现
IIdentity 接口来创建自定义标识类。有关创建自定义标识的详细信息,请参阅本指南后面的扩展默认实现。有关如何使用默认
IIdentity 实现的详细信息,请参阅本指南后面的设计用于授权的身份验证。
使用主体
.NET Framework 提供了链接用户角色和标识的
IPrincipal 接口。所有执行应用程序授权的托管代码都应该使用实现
IPrincipal 的类的对象。例如,
WindowsPrincipal 和
GenericPrincipal 类提供了内置的
IPrincipal 实现。另外,您也可以根据
IPrincipal 创建自己的自定义主体类。
为了提高编码效率,您可以使用本指南的设计用于授权的身份验证一节中介绍的技术,通过使用
Thread 对象的静态
CurrentPrincipal 属性可以将
IPrincipal 对象链接到线程,这样当前线程就可以轻松地访问
IPrincipal 对象了,如以下代码所示:
然后,您可以测试用户是否属于某一特定的角色,从而执行授权检查。为此,可以使用
IPrincipal 接口的
IsInRole 方法,如以下代码所示:
ASP.NET 应用程序处理
IPrincipal 对象的方法与其他基于 .NET 的应用程序的处理方法有所不同。ASP.NET 通过无状态的 HTTP 协议创建会话外观。作为会话的一部分,执行用户请求的所有代码可以通过
HttpContext 对象的
User 属性,使用代表用户的
IPrincipal 对象。Global.asax 文件发生
OnAuthenticate 事件之后,公共语言运库使用
HttpContext.User 值自动更新
Thread.CurrentPrincipal。
ASP.NET 应用程序通常使用
User 属性执行授权检查,如以下代码所示:
注意:手动更改 HttpContext.User 将自动更新在同一 HTTP 上下文环境中执行的所有线程的 Thread.CurrentPrincipal。但是,改变 Thread.CurrentPrincipal 不会影响 HttpContext.User 属性,它只影响为请求中其余内容选择的线程。
有关创建自己的
IPrincipal 类型的详细信息,请参阅本指南后面的扩展默认实现。有关如何使用默认
IPrincipal 实现的详细信息,请参阅本指南后面的设计用于授权的身份验证。
授予使用 IIdentity 和 IPrincipal 对象的权限
使用
IIdentity 对象是一种敏感操作,因为在该操作中可以使用与用户相关的信息。允许应用程序更改当前的
IPrincipal 对象也应该受到保护,因为应用程序授权能力是以当前的主体为基础的。框架要求这些操作具有代码访问安全性权限,从而提供了保护。使用 Caspol.exe 或 .NET Framework 配置工具为需要管理这些对象的应用程序授予
SecurityPermissionAttribute.ControlPrincipal 权限。
默认情况下,所有本地安装的应用程序均具有该权限,因为它们是在“完全信任”的权限设置下运行的。
执行以下方法需要
ControlPrincipal 权限:
- AppDomain.SetThreadPrincipalPolicy()
- WindowsIdentity.GetCurrent()
- WindowsIdentity.Impersonate()
- Thread.CurrentPrincipal()
有关使用 CASPOL 设置安全权限的详细信息,请参阅 MSDN Library 中的 Configuring Security Policy Using the Code Access Security Policy Tool (Caspol.exe)(英文)。
Windows 和公共语言运库之间管理授权的区别
公共语言运库在 Windows 安全结构之上有一个单独的安全结构。Windows 线程具有 Windows 授权用户的令牌,而运行时线程具有代表该用户的
IPrincipal 对象。
因此,在开发代码时,必须至少考虑两种代表用户的安全上下文。例如,设想一个使用窗体身份验证的 ASP.NET 应用程序:默认情况下,ASP.NET 进程在名为“ASPNET”的 Windows 服务帐户(专为应用程序创建的用户帐号)下运行。假设有一位名为
Bill 的用户登录到 Web 站点。
Thread.CurrentPrincipal 属性代表窗体身份验证的用户 Bill。公共语言运库看到以 Bill 运行的线程,于是所有托管代码都将 Bill 看作主体。如果托管代码要求访问系统资源(如文件),则不管
Thread.CurrentPrincipal 的值如何,非托管代码都可以看到 ASPNET Windows 服务帐户。图 5 说明了公共语言运库和操作系统线程之间的授权关系。
图 5:公