| 本章内容 | |
| 目标 | |
| 适用范围 | |
| 如何使用本章内容 | |
| 开始之前 | |
| 设计身份验证和授权策略 | |
| 授权方法 | |
| 传递标识 | |
| 基于角色授权 | |
| 选择身份验证机制 | |
| 总结 |
为分布式 Web 应用程序设计身份验证和授权机制是一项具有挑战性的任务,因为您必须做出许多选择,其结果将几乎影响到您所开发的应用程序的所有组件。应用适当的身份验证和授权设计将有助于降低安全方面的诸多重大风险。在应用程序开发的早期阶段实现上述设计要比在现有应用程序或已部分生成的应用程序中改进一个解决方案要容易得多。
本章介绍在开发分布式 ASP.NET Web 应用程序时您可使用的身份验证和授权机制。其中还介绍了一个过程,它可帮助您选择最适用于您的应用程序的身份验证和授权机制。
本章的目标是:
| • | 了解用来在应用程序的各层之间传递标识的各种选项。 |
| • | 确定要在分布式 Web 应用程序中使用的适当身份验证和授权机制。 |
| • | 在分布式 .NET Web 应用程序中比较和对照身份验证和授权的“模拟/委派”(又称“原始调用方”)和“受信任子系统”这两个模型。 |
| • | 比较和对照基于角色的授权以及基于资源的授权。 |
本章适用于以下产品和技术:
| • | Windows XP 或 Windows 2000 Server (service pack 3) 和更高版本的操作系统 |
| • | Microsoft Internet 信息服务 (IIS) 5.0 和更高版本 |
| • | Microsoft Active Directory |
| • | .NET Framework 版本 1.0 (Service Pack 2) 和更高版本 |
| • | SQL Server 2000 (Service Pack 2) 和更高版本 |
本章介绍了一个过程,用于确定可在您的分布式 Web 应用程序中使用的适当身份验证和授权机制。越早考虑应用程序的身份验证和授权要求,就越易于实现相应的机制,而且您的应用程序可能会越安全。若要学好本章内容:
| • | 阅读第 1 章简介。其中对与分布式 Web 应用程序相关的“身份验证”和“授权”这两个术语进行了定义。 | ||||||||||||||||||
| • | 阅读第 2 章 ASP.NET 应用程序的安全模型。其中概述了在创建分布式 ASP.NET Web 应用程序的过程中所采用的体系结构和技术。它详细介绍了本章中重点讨论的身份验证和授权机制,并定义了授权主题中的两个核心概念:网关和网关守卫。 | ||||||||||||||||||
| • | 阅读下列各章,它们演示了如何实施本章所讨论的身份验证和授权技巧:
|
为分布式 Web 应用程序设计身份验证和授权策略是一项具有挑战性的任务。令人欣慰的是,在应用程序开发的早期阶段,正确设计身份验证和授权将有助于降低许多安全方面的主要风险。
本章将帮助您为应用程序设计合适的授权策略,还将帮助您解答下列关键问题:
| • | 应该在什么地方执行授权,使用什么机制? |
| • | 应该使用什么身份验证机制? |
| • | 应该用 Active Directory® 目录服务来进行身份验证验证,还是应该对照自定义数据存储来验证凭据? |
| • | 使用异类平台和同类平台分别意味着什么?在设计上有哪些需要注意的地方? |
| • | 在应用程序中应该如何表示不使用 Microsoft® Windows® 操作系统的用户? |
| • | 应该如何在应用程序的各层之间传递用户标识?在什么情况应该使用操作系统级别的模拟/委派? |
当您考虑授权时,也必须考虑身份验证。这两个过程紧密相关,其中有以下两个方面的原因:
| • | 第一,任何有意义的授权策略都需要已验证身份的用户。 |
| • | 第二,验证用户标识的方式(具体说,就是如何在应用程序中表示已经过验证的用户标识)将决定您可支配的网关守卫。 |
您可采用下列步骤为应用程序开发身份验证和授权策略:
1. | 确定资源 |
2. | 选择授权策略 |
3. | 选择用于获得资源访问的标识 |
4. | 考虑标识传递 |
5. | 选择身份验证方法 |
6. | 决定如何传递标识 |
确定应用程序需要向客户端公开的资源。典型的资源包括:
| • | Web Server 资源,例如 Web 页、Web 服务、静态资源(HTML 页面和图像)。 |
| • | 数据库资源,例如每个用户的数据或应用程序范围的数据。 |
| • | 网络资源,例如远程文件系统资源和来自目录存储(如 Active Directory)的数据。 |
还必须确定您的应用程序所要访问的系统资源。这些资源与向客户端公开的资源相反,是不公开的。系统资源包括注册表、事件日志和配置文件。
有两种基本的授权策略:
| • | 基于角色。对操作(通常是方法)的访问权限是根据调用方的角色成员身份来提供的。使用角色将应用程序的用户群分为在应用程序内共享相同安全权限的用户组,例如“高级经理”、“经理”和“职员”。用户被映射到角色,如果用户有权执行所请求的操作,那么应用程序将使用固定标识访问资源。这些标识被各自的资源管理器(例如数据库、文件系统等)所信任。 |
| • | 基于资源。单个的资源是通过 Windows ACL 来提供保护的。应用程序在访问资源之前先模拟调用方,这使操作系统可以执行标准访问检查。对资源的所有访问都是使用原调用方的安全性上下文执行的。此模拟方法严重影响应用程序的可伸缩性,因为这意味着在应用程序的中间层无法高效地使用连接池。 |
对于绝大多数在伸缩性方面有很高要求的 .NET Web 应用程序来说,基于角色的授权方法是最佳选择。而对于某些规模较小的 Intranet 应用程序,如果它们所提供的各用户内容来自可通过对单个用户执行 Windows ACL 检查来提供访问权限的资源(例如文件),则可采用基于资源的授权方法。
在进行基于角色的授权时,所推荐使用的和常见的做法是:
| • | 在前端 Web 应用程序中验证用户标识。 |
| • | 将用户映射到角色。 |
| • | 根据角色成员身份授权对操作(而非直接对资源)的访问权限。 |
| • | 使用固定的服务标识,访问必要的后端资源(用来支持所请求的和授权的操作)。后端资源管理器(例如数据库)“信任”应用程序授权调用方,而且愿意将权限授予被信任的服务标识或其他标识。 例如,数据库管理员可以将访问权限只授予某个特定的人力资源应用程序(而不是授予单个的用户)。 |
注意:基于应用程序级别角色的授权方法仍然需要使用基于资源的授权保护系统级别的资源,例如配置文件、注册表项等等。
更多信息
| • | 有关这两种截然不同的授权方法,请参见本章后面的授权方法。 |
| • | 有关基于角色的授权以及各种可以使用的角色类型,请参见本章后面的基于角色授权。 |
请回答这样的问题:“谁将访问资源”?
选择在访问应用程序各个级别的资源时所应使用的标识。这些资源可以由基于 Web 的应用程序来访问,还可以由 Web 服务、企业服务和 .NET Remoting 组件来访问。一般来讲,用于访问资源的标识可以是以下几类:
| • | 原调用方标识。它采用一种模拟/委派模型,即可以获得原调用方的标识,然后将此标识在系统的每一层之间传递。委派因素是用于确定身份验证机制的一个重要标准。 | ||||
| • | 进程标识。这是默认情况(没有具体的模拟)。使用当前进程标识访问本地资源并进行下游调用。这一方法是否可行取决于所要跨越的边界,因为进程标识必须被目标系统所识别。 这意味着要用以下方式之一来进行调用:
| ||||
| • | 服务帐户。这一方法使用一个(固定的)服务帐户。例如:
| ||||
| • | 自定义标识。当没有可用的 Windows 帐户时,您可以构建自己的标识(使用 IPrincipal 和 IIdentity 实现),它将包含您指定的安全性上下文的具体内容。例如,它可以包含角色列表、唯一标识符或任何其他类型的自定义信息。 |
要支持按用户进行授权、审核和按用户检索数据,您可能需要在不同的应用程序层中和在不同计算机之间传递原调用方标识。例如,如果某个后端资源管理器需针对每个调用方进行授权,则调用方的标识就必须传递给该资源管理器。
您要根据系统的资源管理器授权要求和审核要求来确定需要在应用程序中传递的标识。
影响身份验证方法选择有两个关键因素:第一,也是最重要的因素是,应用程序的用户群的特点(他们使用哪一类浏览器以及他们是否有 Windows 帐户),第二是,应用程序的模拟/委派要求和审核要求。
更多信息
有关帮助您为应用程序选择身份验证机制的更多具体注意事项,请参见本章后面的选择身份验证机制。
可以在应用程序级传递标识(以提供安全性上下文),或者在操作系统级传递标识和安全性上下文。
要在应用程序级传递标识,请使用方法和存储过程参数。应用程序标识传递支持:
| • | 使用受信任的查询参数来按用户检索数据 |
SELECT x,y FROM SomeTable WHERE username="bob"
| • | 在任何应用程序层中自定义审核 |
操作系统标识传递支持:
| • | 平台级审核(例如,Windows 审核和 SQL Server 审核) |
| • | 基于 Windows 标识按用户进行授权 |
要在操作系统级传递标识,可以使用模拟/委派模型。在某些情况下,可以使用 Kerberos 委派,而在另外一些情况下(或许是环境不支持 Kerberos),可能需要使用其他方法,例如使用基本身份验证。在基本身份验证中,用户凭据可供服务器应用程序使用,而且还可以用于访问下游网络资源。
更多信息
有关传递标识以及如何利用网络凭据获得模拟令牌(即支持委派)的更多信息,请参见本章后面的传递标识。
有两种基本授权方法:
| • | 基于角色。用户被划分为应用程序定义的逻辑角色。在应用程序中,某一特定角色的成员将共享相同的权限。对操作(通常由方法调用表示)的访问是根据调用方的角色成员身份进行授权的。 |
| • | 基于资源。各个资源是通过 Windows ACL 得到保护的。ACL 决定了哪些用户可以访问资源,还决定每个用户可以执行哪类操作(读取、写入、删除等)。 |
在基于角色(或操作)的安全方法中,对操作(而非后端资源)的访问是根据调用方的角色成员身份进行授予的。角色(在设计应用程序时进行分析并加以定义)被用作逻辑容器,它们将应用程序中共享同一安全权限(或功能)的用户集合在一起。用户被映射到应用程序中的角色,而角色成员身份用来控制对应用程序公开的特定操作(方法)的访问权。
在应用程序的什么位置映射角色是设计中需要考虑的一个关键因素;例如
| • | 第一种情况是,可以在后端资源管理器(如数据库)中映射角色。它要求将原调用方的安全性上下文通过应用程序的各层一直传递到后端数据库。 |
| • | 另一种情况是在前端 Web 应用程序中映射角色。如果是运用这种方法,那么就要通过固定标识来访问下游资源管理器,而这些资源管理器对这些标识进行了授权并愿意信任它们。 |
| • | 第三种选择是在前端层和后端层之间的某个位置进行角色映射;例如:在中间层企业服务应用程序中。 |
在多层 Web 应用程序中,使用受信任的标识访问后端资源管理器为应用程序的伸缩性提供了更多的机会(这得归功于连接池)。另外,使用受信任的标识减少了在操作系统级传递原调用方安全性上下文的需要,而这本来可能是不太可能(即使可能,也是很难)实现的。
基于资源的授权方法依赖于 Windows ACL 和操作系统的基本访问控制机制。应用程序模拟调用方,将执行访问检查的任务留给了与特定资源管理器(文件系统、数据库等)关联的操作系统。
这种方法通常最适合于以下应用程序:您利用它们来访问可通过 Windows ACL 加以个别保护的资源(如文件资源)。FTP 应用程序或简单的数据驱动 Web 应用程序就属于这类应用程序。如果被请求资源中的数据需要从多个不同来源(如多个数据库、数据库表、外部应用程序或 Web 服务等)获取和合并,那么这种方法就显得力不从心了。
基于资源的方法还依赖于传递到应用程序后端资源管理器的原调用方的安全性上下文。这可能需要进行复杂的配置,而且会大大降低多层应用程序增加受访的能力,因为使用这种方法,您就无法在应用程序中间层有效运用集合功能(如数据库连接池)。
通过研究 .NET Web 应用程序(以及通常所见的分布式多层应用程序)最常用的两种资源访问安全模型,我们可以看到有两种截然不同的授权方法。这两种模型是:
| • | 受信任的子系统模型 |
| • | 模拟/委派模型 |
这两种模型在安全性和可伸缩性方面都各有优缺点。以下各节将介绍这两种模型。
在此模型中,中间层服务使用固定标识访问下游服务和资源。原调用方的安全性上下文不通过操作系统级的服务进行传递,但应用程序可以选择在应用程序级传递原调用方标识。它这样做可能是需要支持后端审核要求,或支持按用户访问数据和授权。
该模型之所以得此名称,是因为下游服务(或许是数据库)信任上游服务,让其授权调用方。图 3.1 显示了此模型。请特别注意信任边界。在本示例中,数据库“信任”中间层让其授权调用方,并只允许经过授权的调用方使用受信任的标识访问数据库。

图 3.1
受信任的子系统模型
在受信任的子系统模型中,资源访问步骤如下:
| • | 对用户进行身份验证 |
| • | 将用户映射到角色 |
| • | 根据角色成员身份进行授权 |
| • | 使用固定的受信任标识访问下游资源管理器 |
用于访问下游系统和资源管理器的固定标识通常由预配置的 Windows 帐户(称为服务帐户)提供。在 Microsoft SQL Server 资源管理器中,这意味着对 SQL Server 使用 Windows 身份验证。
另一种情况是,某些应用程序使用指定的 SQL 帐户(由连接字符串中的用户名和密码指定)访问 SQL Server。在这种情况下,必须对数据库进行配置,以使用 SQL 身份验证。
有关与 SQL Server 通信时 Windows 身份验证和 SQL 身份验证的相对优点,请参见第 12 章数据访问安全性。
有些资源管理器可能需要根据调用方的角色成员身份,执行更进一步细分的授权。例如,您可能有两组用户,一组应被授权执行读取/写入操作,另一组则应被授权执行只读操作。
请看下面的 SQL Server 方法:
| • | 创建两个 Windows 帐户,一个用于读取操作,一个用于读取/写入操作。
|
| • | 将每个帐户映射到一个 SQL Server 用户定义的数据库角色,并且为每个角色建立必要的数据库权限。 |
| • | 将用户映射到应用程序中的角色,并在连接到数据库之前使用角色成员身份确定所要模拟的帐号。 |
图 3.2 中显示了这一方法。

图 3.2
使用多种标识访问数据库以支持更细分的授权
在此模型中,服务或组件(通常在逻辑业务服务层中的某个位置)在访问下一个下游服务之前模拟客户端的标识(使用操作系统级模拟)。如果下一个服务在同一台计算机上,模拟就足够了。如果下游服务位于远程计算机上,则需要使用委派。
委派的结果是,用于访问下游资源的安全性上下文是客户端的安全性上下文。使用此模型通常是由于以下几种原因:
| • | 它允许下游服务借助原调用方标识来按调用方进行授权。 |
| • | 它允许下游服务使用操作系统级审核功能。 |
这一方法的一个具体示例是,中间层企业服务组件可能会在访问数据库之前模拟调用方。将使用依赖于原调用方的安全性上下文的数据库连接访问数据库。在此模型中,数据库对每个调用方进行身份验证,并基于指派给各调用方的标识(或调用方的 Windows 组成员身份)的权限做出授权决定。图 3.3 显示了模拟/委派模型。

图 3.3
模拟/委派模型
绝大多数 Internet 应用程序和大规模的 Intranet 应用程序都使用受信任的子系统模型,其主要原因是可伸缩性。模拟模式往往用于可伸缩性不是主要问题的小规模应用程序和那些审核(由于不可否认性原因)是关键问题的应用程序。
模拟/委派模型最主要的优点是审核(接近数据)。审核允许管理员跟踪试图访问特定资源的用户。通常情况下,如果审核是在资源访问的精确时间、由访问资源的同一例程产生的,则认为审核最具权威性。
模拟/委派模型为下游资源访问维护用户的安全性上下文,从而为此提供支持。这允许后端系统权威性地记录用户和所请求的访问。
与模拟/委派模型有关的缺点包括:
| • | 技术挑战。大多数安全服务提供程序不支持委派,Kerberos 是个重要的例外。
|
| • | 可伸缩性。模拟/委派模型意味着您无法高效地使用数据库连接池,因为访问数据库时所使用的连接与原调用方的不同安全性上下文密切相关。这极大限制了应用程序扩展到大量用户的能力。 |
| • | 更多管理工作。维护后端资源上的 ACL 时,需要授予每一个用户适当的访问级别。随着后端资源的数量增加(因此用户数量也增加)时,需要执行大量的工作来管理 ACL。 |
受信任的子系统模型具有以下优点:
| • | 可伸缩性。受信任子系统模型支持连接池,这是应用程序可伸缩性的一项基本要求。连接池允许多个客户端重用可用的池连接。它非常适合于此模式,因为无论调用方的标识是什么,对后端资源的所有访问都将使用服务帐户的安全性上下文。 |
| • | 后端 ACL 管理工作减到最少。只有服务帐户访问后端资源(例如数据库)。仅针对此标识来配置 ACL。 |
| • | 用户无法直接访问数据。在受信任的子系统模型中,只有中间层服务帐户才有权访问后端资源。因此,如果用户不通过应用程序(并且以应用程序授权为准),就无法直接访问后端数据。 |
受信任的子系统模型有几个缺点:
| • | 审核。要在后端执行审核,可以(在应用程序级)将原调用方标识以明文方式传递到后端,并在那里执行审核。您必须信任中间层,而且确实有潜在的拒绝风险。或者,可以在中间层生成审核记录,然后将它与后端审核记录相关联(为此,必须确保服务器时钟是同步的)。 |
| • | 增加了服务器攻击危险。在受信任的子系统模型中,中间层服务被授予对后端资源的广泛访问权限。因此,存在安全隐患的中间层服务可能使攻击者更容易获得对后端资源的广泛访问权限。 |
分布式应用程序可划分为多个安全子系统。例如,前端 Web 应用程序、中间层 Web 服务、远程组件和数据库代表 4 个不同的安全子系统。每个安全子系统都执行身份验证和授权。
必须识别以下子系统:它们必须将调用方的标识(以及相关的安全性上下文)传递到下一个下游子系统,以便针对原调用方进行授权。
标识传递策略包括使用操作系统的委派功能或在应用程序级传递票证和/或凭据。例如:
| • | 要在应用程序级传递标识,一般使用方法参数或存储过程参数传递凭据(或票证)。 注意:携带已通过身份验证的调用方的标识的 GenericPrincipal 对象不会在进程间自动传递。这需要自定义代码。 可以向存储过程传递允许您检索和处理特定用户数据的参数。例如: SELECT CreditLimit From Table Where UserName="Bob" 此方法有时称为“受信任的查询参数”方法。 |
| • | 操作系统标识传递需要采用委派,它是模拟的一种扩展形式。 |
在通常情况下,服务器应用程序中的线程使用服务器进程的安全性上下文运行。组成进程安全性上下文的属性由进程的登录会话维护,并由进程级 Windows 访问令牌公开。访问本地资源和远程资源时均将使用进程级安全性上下文(由用于运行服务器进程的 Windows 帐户决定)。
为服务器应用程序配置了模拟后,模拟令牌附加到用于处理请求的线程上。模拟令牌代表已通过身份验证的调用方(或匿名用户)的安全性上下文。访问本地资源时均将使用线程模拟令牌(导致使用调用方的安全性上下文)。
如果服务器应用程序线程试图访问远程资源,则需要委派。具体说来,被模拟的调用方的令牌必须有网络凭据。如果没有,所有远程资源访问都将以匿名用户 (AUTHORITY\ANONYMOUS LOGON) 的身份执行。
有许多因素决定是否可以委派安全性上下文。表 3.1 显示了各种 IIS 身份验证类型,并且对于每一种类型指出了是否可以委派已通过身份验证的调用方的安全性上下文。
表 3.1:IIS 身份验证类型
| 身份验证类型 | 能否委派 | 备注 |
匿名 | 视情况而定 | 如果匿名帐户(默认情况下是 IUSR_MACHINE)在 IIS 中被配置为本地帐户,则除非本地(Web 服务器)计算机和远程计算机有相同的本地帐户(具有匹配的用户名和密码),否则就不能委派它。 |
基本 | 是 | 如果对本地帐户使用“基本”身份验证,并且本地和远程计算机上的本地帐户相同,则可以委派它。域帐户也可以委派。 |
摘要式 | 否 |
|
集成 Windows | 视情况而定 | 集成 Windows 身份验证将导致 NTLM 或 Kerberos(具体取决于客户机和服务器的操作系统版本)。 |
客户端证书 | 视情况而定 | 如果与 IIS 证书映射一起使用,并且证书映射到一个在远程计算机重复的本地帐户或映射到域帐户,则可以委派。 |
重要说明:Windows 2000 下的 Kerberos 委派不受限制。也就是说,用户或许能在多台远程计算机之间制造多个网络跃点。为消除这种潜在的安全风险,应该限制域帐户的访问范围,方法是从“域用户”组中删除此帐户,只允许使用此帐户登录到特定的计算机。
大多数 .NET Web 应用程序都使用基于角色的授权方法。您需要考虑不同的角色类型,选择最适合您的应用方案的方法。以下选项可供您选择:
| • | .NET 角色 |
| • | 企业服务 (COM+) 角色 |
| • | SQL Server 用户定义的数据库角色 |
| • | SQL Server 应用程序角色 |
.NET 角色非常灵活,它围绕 IPrincipal 对象展开,这些对象包含已通过验证的标识所属的角色列表。.NET 角色可以在 Web 应用程序、Web 服务或驻留在 ASP.NET 中(并使用 HttpChannel 进行访问)的远程组件中使用。
可以通过 .NET 角色以两种方式进行授权:一种是以声明方式用 PrincipalPermission 命令;一种是以编程方式,在代码中用强制的 PrincipalPermission 命令或 IPrincipal.IsInRole 方法。
如果您的应用程序使用 Windows 身份验证,ASP.NET 会自动构建一个 WindowsPrincipal ,并将其附加到当前 Web 请求的上下文(使用 HttpContext.User)。待身份验证进程完成并且 ASP.NET 将该对象附加到当前请求中后,该对象将用于后面所有基于 .NET 角色的授权。
已验证身份的调用方的 Windows 组成员身份用于确定角色集。在 Windows 身份验证中,.NET 角色与 Windows 组相同。
如果您的应用程序使用除 Windows 身份验证之外的身份验证机制(例如窗体身份验证或 Passport 身份验证),您必须编写代码以创建 GenericPrincipal 对象(或自定义的 IPrincipal 对象),并使用从自定义身份验证数据存储(如 SQL Server 数据库)中获得的角色集填充它。
基于 .NET 角色的安全机制是可扩展的。您可以自己开发用来实现 IPrincipal 和 IIdentity 的类,并提供您自己的基于角色的扩展授权功能。
只要自定义的 IPrincipal 对象(包含从自定义数据存储中获得的角色)附加到当前请求的上下文(使用 HttpContext.User)之上,就保证了基本的角色检查功能。
通过实现 IPrincipal 接口,可以确保声明形式的和强制形式的 PrincipalPermission 命令都适用于您的自定义标识。此外,您还可以实现扩展的角色语义;例如,提供允许您测试和断言多个角色的成员身份的其他方法,例如 IsInMultipleRoles (string [] roles)。
更多信息
| • | 有关基于 .NET 角色的授权的详细信息,请参见第 8 章 ASP.NET 安全性。 |
| • | 有关创建 GenericPrincipal 对象的详细信息,请参阅本指南中的如何利用窗体身份验证创建 GenericPrincipal 对象。 |
使用企业服务 (COM+) 角色可以将访问权限检查推至中间层,并且允许您在连接到后端数据库时使用数据库连接池。但是,对于有意义的基于企业服务 (COM+) 角色的授权,您的前端 Web 应用程序必须模拟原调用方标识并将其传递到企业服务应用程序(使用 Windows 访问令牌)。为此,必须在 Web 应用程序的 Web.config 文件中放入以下条目。
<authentication mode="Windows" /> <identity impersonate="true" />
如果在方法级使用声明性检查(以确定哪些用户可以调用哪些方法)就足够的话,可以使用“组件服务”管理工具部署应用程序和更新角色成员身份。
如果需要用方法代码以编程方式进行检查,您将会失去企业服务 (COM+) 角色的一些管理和部署优势,因为角色逻辑是硬编码的。
在这一方法中,您在数据库中创建角色、基于角色分配权限并将 Windows 组帐户和用户帐户映射到角色。此方法要求您将调用方的标识传递到后端(如果您使用的是首选的 Windows 身份验证,而非 SQL Server身份验证)。
在这一方法中,对数据库中的角色授予权限,但是 SQL Server 应用程序角色不包含用户帐户或组帐户。因此,您将丢失原调用方的粒度。
利用应用程序角色,您将针对具体的应用程序进行授权(与用户组授权相对)。此应用程序使用接受角色名称和密码的内置存储过程激活角色。这一方法的一个主要缺点是它要求应用程序安全管理凭据(角色名称和关联的密码)。
更多信息
有关 SQL Server 用户定义的数据库角色和应用程序角色的详细信息,请参阅第 12 章数据访问安全性。
下表显示了 .NET 角色和企业服务 (COM+) 角色的功能对比。
表 3.2: 企业服务角色与 .NET 角色的对比
| 功能 | 企业服务角色 | .NET 角色 |
管理 | “组件服务”管理工具 | 自定义 |
数据存储 | COM+ 目录 | 自定义数据存储(如 SQL Server 或 Active Directory) |
声明性 | 是 | 是 |
强制性 | 是 | 是 |
类、接口和方法级别的细化程度 | 是 | 是 |
可扩展 | 否 | 是 |
可供所有 .NET 组件使用 | 只供派生自 ServicedComponent 基类的组件使用 | 是 |
角色成员身份 | 角色包含 Windows 组帐户或用户帐户 | 当使用 WindowsPrincipals 时,角色是 Windows 组 没有额外的抽象级别 |
需要显式接口实现 | 是 | 否 |
您可以使用 .NET 角色保护以下各项的安全:
| • | 文件 |
| • | 文件夹 |
| • | Web 页(.aspx 文件) |
| • | Web 服务(.asmx 文件) |
| • | 对象 |
| • | 方法和属性 |
| • | 方法中的代码块 |
您可以使用 .NET 角色保护操作(由方法和属性执行)和特定代码块的事实,意味着您可以保护您的应用程序对本地和远程资源的访问。
注意:使用 UrlAuthorizationModule 保护上述列表的前四项(文件、文件夹、Web 页和 Web 服务),UrlAuthorizationModule 可以利用调用方的角色成员身份(以及调用方的标识)做出授权决定。
如果您使用 Windows 身份验证,系统会为您完成使用 .NET 角色所需的大部分工作。ASP.NET 会构建 WindowsPrincipal 对象,用户的 Windows 组成员身份确定关联的角色集。
要在非 Windows 身份验证机制中使用 .NET 角色,必须编写代码执行以下操作:
| • | 捕获用户的凭据。 |
| • | 对照自定义数据存储(如 SQL Server 数据库)验证用户的凭据。 |
| • | 检索角色列表,构建 GenericPrincipal 对象并将它与当前的 Web 请求关联。 |
更多信息
有关为窗体身份验证创建 GenericPrincipal 对象所涉及过程的详细信息,请参阅第 8 章 ASP.NET 安全性。
可使用下列类型的 .NET 角色检查:
重要说明:.NET 角色检查依赖于 IPrincipal 对象(表示已验证身份的用户)是否与当前请求关联。对于 ASP.NET Web 应用程序,IPrincipal 对象必须附加到 HttpContext.User。对于 Windows 窗体应用程序,IPrincipal 对象必须附加到 Thread.CurrentPrincipal。
| • | 手动角色检查。对于细分授权,您可以调用 IPrincipal.IsInRole 方法,以便基于调用方的角色成员身份授予对特定代码块的访问权限。当检查角色成员身份时,AND 和 OR 逻辑都可以使用。 |
| • | 声明性角色检查(方法入口)。您可以使用 PrincipalPermissionAttribute 类(可以简写为 PrincipalPermission)对方法进行注释,以声明方式要求调用方提供角色成员身份。这些检查只支持 OR 逻辑。例如,您可以要求调用方至少有一个特定的角色(例如,调用方必须是出纳或经理)。用声明性检查无法指定调用方必须是经理和出纳。 |
| • | 强制性角色检查(在方法内检查)。可以在代码中调用 PrincipalPermission.Demand 来执行细分的授权逻辑。支持 AND 和 OR 等逻辑操作。 |
下面的代码片段显示了一些使用编程式、声明性和强制性方法进行角色检查的示例。
授权 Bob 执行操作:
注意:尽管可以对用户进行个别授权,但通常应该基于角色成员身份进行授权,这使您可以授权用户组,而组中的所有用户在应用程序中将具有相同的权限。
| • | 直接检查用户名 GenericIdentity userIdentity = new GenericIdentity("Bob");
if (userIdentity.Name=="Bob")
{
}
|
| • | 声明性检查 [PrincipalPermissionAttribute(SecurityAction.Demand, User="Bob")]
public void DoPrivilegedMethod()
{
}
|
| • | 强制性检查 PrincipalPermission permCheckUser = new PrincipalPermission(
"Bob", null);
permCheckUser.Demand();
|
授权出纳执行操作:
| • | 直接检查角色名称 GenericIdentity userIdentity = new GenericIdentity("Bob");
// 角色名将从自定义数据存储中检索
string[] roles = new String[]{"Manager", "Teller"};
GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity,
roles);
if (userPrincipal.IsInRole("Teller"))
{
}
|
| • | 声明性检查 [PrincipalPermissionAttribute(SecurityAction.Demand, Role="Teller")]
void SomeTellerOnlyMethod()
{
}
|
| • | 强制性检查 public SomeMethod()
{
PrincipalPermission permCheck = new PrincipalPermission(
null,"Teller");
permCheck.Demand();
// 只有 Teller(出纳)可以执行以下代码
// 安全异常情况中没有生成 Teller 角色的成员
. . .
}
|
授权经理或出纳执行操作:
| • | 直接检查角色名称 if (Thread.CurrentPrincipal.IsInRole("Teller") ||
Thread.CurrentPrincipal.IsInRole("Manager"))
{
// 执行特权操作
}
|
| • | 声明性检查 [PrincipalPermissionAttribute(SecurityAction.Demand, Role="Teller"),
PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DoPrivilegedMethod()
{
...
}
|
| • | 强制性检查 PrincipalPermission permCheckTellers = new PrincipalPermission(
null,"Teller");
PrincipalPermission permCheckManagers = new PrincipalPermission(
null,"Manager");
(permCheckTellers.Union(permCheckManagers)).Demand();
|
只授权那些既是经理又是出纳的用户执行操作:
| • | 直接检查角色名称 if (Thread.CurrentPrincipal.IsInRole("Teller") &&
Thread.CurrentPrincipal.IsInRole("Manager"))
{
// 执行特权操作
}
|
| • | 声明性检查 不能用 .NET 角色以声明方式执行 AND 检查。将 PrincipalPermission 命令堆叠在一起使用会产生逻辑 OR。 |
| • | 强制性检查 PrincipalPermission permCheckTellers = new PrincipalPermission(
null,"Teller");
permCheckTellers.Demand();
PrincipalPermission permCheckManagers = new PrincipalPermission(
null, "Manager");
permCheckManagers.Demand();
|
本节提供指导信息,旨在帮助您选择适合常见应用方案的身份验证机制。您应该从考虑以下问题着手:
| • | 标识。仅当应用程序用户的 Windows 帐户可由受信任的机构进行身份验证,并且应用程序的 Web 服务器可以访问该机构时,才适合使用 Windows 身份验证机制。 | ||||
| • | 凭据管理。Windows 身份验证的一个主要优点是它使您可以让操作系统负责凭据管理。在非 Windows 方法(如“窗体”身份验证)中,您必须仔细考虑在何处以及如何存储用户凭据。两种最常见的方法是:
有关将 SQL Server 用作凭据存储的安全注意事项的详细信息,请参见第 12 章数据访问安全性。 有关对照自定义数据存储(包括 Active Directory)使用“窗体”身份验证的详细信息,请参见第 8 章 ASP.NET 安全性。 | ||||
| • | 标识传递。您需要实现模拟/委派模型并在操作系统级的各层间传递原调用方的安全性上下文吗?例如,支持审核或每用户(粒度)授权。如果需要,您需要能够模拟调用方并将它们的安全性上下文委派给下一个下游子系统,详见本章前面的传递标识一节。 | ||||
| • | 浏览器类型 您的用户是否都安装了 Internet Explorer,或者您是否需要支持使用混合浏览器类型的用户群?表 3.3 阐释了哪些身份验证机制要求 Internet Explorer 浏览器,以及哪些身份验证机制支持多种常见的浏览器类型。 |
表 3.3: 身份验证对浏览器的要求
| 身份验证类型 | 是否要求 Internet Explorer | 备注 |
窗体 | 否 |
|
Passport | 否 |
|
集成 Windows(Kerberos 或 NTLM) | 是 | Kerberos 还要求在客户机和服务器上安装 Windows 2000 或更高版本的操作系统以及为委派配置的帐户。有关详细信息,请参见本指南中的如何为 Windows 2000 实现 Kerberos 委派。 |
基本 | 否 | 基本身份验证是几乎所有浏览器都支持的 HTTP 1.1 协议的一部分。 |
摘要式 | 是 |
|
证书 | 否 | 客户端要求 X.509 证书 |
Internet 方案的基本假设条件是:
| • | 用户在服务器的域中或可由服务器访问的受信任域中没有 Windows 帐户。 |
| • | 用户没有客户端证书。 |
图 3.4 显示了一个为 Internet 方案选择身份验证机制的决策树。

图 3.4
为 Internet 应用程序选择身份验证机制
有关 Web 服务安全以及 WS 安全规范(全球 XML 体系结构 (GXA) 提案的一部分)的详细信息,请参见第 10 章 Web 服务安全性。
本节概述了窗体身份验证和 Passport 身份验证的相对优点。
窗体身份验证的优点
| • | 支持对照自定义数据存储(通常为 SQL Server 数据库或 Active Directory)进行身份验证。 |
| • | 支持基于角色授权(包括从数据存储中查找角色)。 |
| • | 与 Web 用户界面无缝集成。 |
| • | ASP.NET 构成基础结构的大部分。与传统的 ASP 比较,需要的自定义代码相对较少。 |
Passport 身份验证的优点
| • | Passport 是一个集中式解决方案。 |
| • | 它解决了应用程序中的凭据管理问题。 |
| • | 它可用于基于角色的授权方案。 |
| • | 非常安全,因为它建立在加密技术之上。 |
更多信息
| • | 有关 Web 服务身份验证方法的详细信息,请参见第 10 章 Web 服务安全性。 |
| • | 有关在 SQL Server 中使用窗体身份验证的详细信息,请参见本指南的如何将窗体身份验证用于 SQL Server 2000。 |
图 3.5 显示的决策树可协助人们为 Intranet 和 Extranet 应用方案选择身份验证机制。

图 3.5
为 Intranet 和 Extranet 应用程序选择身份验证机制
下表对目前可用的身份验证机制进行了比较。
表 3.4: 可用的身份验证方法
| 基本 | 摘要式 | NTLM | Kerberos | 证书 | 窗体 | Passport | |
用户需要在服务器的域中有 Windows 帐户 | 是 | 是 | 是 | 是 | 否 | 否 | 否 |
支持委派* | 是 | 否 | 否 | 是 | 可以 | 是 | 是 |
需要 Win2K 客户端和服务器 | 否 | 是 | 否 | 是 | 否 | 否 | 否 |
凭据以明文传递(需要 SSL) | 是 | 否 | 否 | 否 | 否 | 是 | 否 |
支持非 IE 浏览器 | 是 | 否 | 否 | 否 | 是 | 是 | 是 |
* 有关详细信息,请参见本章前面传递标识一节中的“委派”主题。
设计分布式应用程序身份验证和授权方法是一项具有挑战性的任务。在应用程序开发的早期设计阶段,正确设计身份验证和授权有助于减少很多重大的安全风险。下面对本章的内容进行总结:
| • | 使用受信任的子系统资源访问模型可获得数据库连接池的种种好处。 |
| • | 如果您的应用程序没有使用 Windows 身份验证,请使用 .NET 角色检查提供授权。对照自定义数据存储验证凭据,检索角色列表并创建 GenericPrincipal 对象。将该对象与当前 Web 请求 (HttpContext.User) 关联。 |
| • | 如果您的应用程序使用的是 Windows 身份验证但没有使用企业服务,则请使用 .NET 角色。请记住,对于 Windows 身份验证,.NET 角色即为 Windows 组。 |
| • | 如果您的应用程序使用了 Windows 身份验证和企业服务,请考虑使用企业服务 (COM+) 角色。 |
| • | 对于使用企业服务 (COM+) 角色的有意义的基于角色授权,原调用方标识必须传递到企业服务应用程序。如果从 ASP.NET Web 应用程序中调用企业服务应用程序,这意味着 Web 应用程序必须使用 Windows 身份验证并且配置为使用模拟。 |
| • | 用 PrincipalPermission 属性对方法进行注释,以声明方式要求调用方提供角色成员身份。如果调用方没有指定的角色成员身份,则不会调用方法并且会产生安全异常。 |
| • | 在方法代码内调用 PrincipalPermission.Demand (或使用 IPrincipal.IsInRole),以实现细分授权决策。 |
| • | 考虑实现自定义 IPrincipal 对象以获得额外的角色检查语义。 |