Windows身份验证给开发者提供了一个使用Windows平台及NTFS文件系统内置安全特性的方式。它也利用了内置于IIS的安全特性。通过使用Windows身份验证,可以使用很少,甚至不用代码就可以构建一个高安全等级的asp.net应用程序。不过只有客户端使用windows平台,并且在web服务器上,或者web服务器从属的Windows域上有一个账户,Windows身份验证才起作用。
有五种类型的Windows身份验证:
1、基本身份验证(Basic authentication);
2、摘要式身份验证(Digest authentication);
3、集成Windows身份验证(Integrated Windows authentication);
4、IIS的客户证书映射验证(IIS Client Certificate Mapping authentication);
5、活动目录的客户证书映射验证(Active Directory Client Certificate Mapping authentication)。
如果不止一种身份验证类型被启用,比方说它们都被勾选中,那么IIS会首先尝试证书映射(Certificate Mapping),然后是集成Windows身份验证。如果验证失败,那么会接着尝试摘要式身份验证。最后,如果其他所有验证都失败,就会使用基本身份验证。换句话说,系统会首先尝试最安全的验证选项,然后逐级向低安全级别的验证选项过渡。
本教程阐释在 ASP.NET 2.0 版中,IIS 集成Windows 身份验证以及 ASP.NET Windows 身份验证的工作机制。同时,阐释 NTLM 和 Kerberos 身份验证的工作机制。此外,本教程还阐释 WindowsAuthenticationModule 类如何构造 WindowsPrincipal 和 WindowsIdentity 对象,然后将这些对象附加到当前的 ASP.NET Web 请求以表示经过身份验证的用户。
概述
身份验证是一个验证客户端身份的过程,通常采用指定的第三方授权方式。客户端可能是最终用户、计算机、应用程序或服务。客户端的标识称为安全原则。为了使用服务器应用程序进行验证,客户端提供某种形式的凭据来允许服务器验证客户端的标识。确认了客户端的标识后,应用程序可以授予执行操作和访问资源的原则。
如果应用程序使用 Active Directory 用户存储,则应该使用集成 Windows 身份验证。对 ASP.NET 应用程序使用集成 Windows 身份验证时,最好的方法是使用 ASP.NET 的 Windows 身份验证提供程序附带的 Internet 信息服务 (IIS) 身份验证方法。使用该方法,将自动创建一个 WindowsPrincipal 对象(封装一个 WindowsIdentity 对象)来表示经过身份验证的用户。您无需编写任何身份验证特定的代码。
ASP.NET 还支持使用 Windows 身份验证的自定义解决方案(避开了 IIS 身份验证)。例如,可以编写一个根据 Active Directory 检查用户凭据的自定义 ISAPI 筛选器。使用该方法,必须手动创建一个 WindowsPrincipal 对象。
ASP.NET 身份验证
IIS 向 ASP.NET 传递代表经过身份验证的用户或匿名用户帐户的令牌。该令牌在一个包含在 IPrincipal 对象中的 IIdentity 对象中维护,IPrincipal 对象进而附加到当前 Web 请求线程。可以通过 HttpContext.User 属性访问 IPrincipal 和 IIdentity 对象。这些对象和该属性由身份验证模块设置,这些模块作为 HTTP 模块实现并作为 ASP.NET 管道的一个标准部分进行调用,如图 3 所示。
图 3. ASP.NET 管道
ASP.NET 管道模型包含一个 HttpApplication 对象、多个 HTTP 模块对象,以及一个 HTTP 处理程序对象及其相关的工厂对象。HttpRuntime 对象用于处理序列的开头。在整个请求生命周期中,HttpContext 对象用于传递有关请求和响应的详细信息。
有关 ASP.NET 请求生命周期的详细信息,请参阅"ASP.NET Life Cycle",网址是 http://msdn2.microsoft.com/library/ms227435(en-US,VS.80).aspx。
身份验证模块
ASP.NET 2.0 在计算机级别的 Web.config 文件中定义一组 HTTP 模块。其中包括大量身份验证模块,如下所示:
以下是引用片段: <httpModules> <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" /> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" /> </httpModules> |
只加载一个身份验证模块,这取决于该配置文件的 authentication 元素中指定了哪种身份验证模式。该身份验证模块创建一个 IPrincipal 对象并将它存储在 HttpContext.User 属性中。这是很关键的,因为其他授权模块使用该 IPrincipal 对象作出授权决定。
当 IIS 中启用匿名访问且 authentication 元素的 mode 属性设置为 none 时,有一个特殊模块将默认的匿名原则添加到 HttpContext.User 属性中。因此,在进行身份验证之后,HttpContext.User 绝不是一个空引用(在 Visual Basic 中为 Nothing)。
WindowsAuthenticationModule
如果 Web.config 文件包含以下元素,则激活 WindowsAuthenticationModule 类。
以下是引用片段: <authentication mode="Windows" /> |
WindowsAuthenticationModule 类负责创建 WindowsPrincipal 和 WindowsIdentity 对象来表示经过身份验证的用户,并且负责将这些对象附加到当前 Web 请求。
对于 Windows 身份验证,遵循以下步骤:
•WindowsAuthenticationModule 使用从 IIS 传递到 ASP.NET 的 Windows 访问令牌创建一个 WindowsPrincipal 对象。该令牌包装在 HttpContext 类的 WorkerRequest 属性中。引发 AuthenticateRequest 事件时,WindowsAuthenticationModule 从 HttpContext 类检索该令牌并创建 WindowsPrincipal 对象。HttpContext.User 用该 WindowsPrincipal 对象进行设置,它表示所有经过身份验证的模块和 ASP.NET 页的经过身份验证的用户的安全上下文。
•WindowsAuthenticationModule 类使用 P/Invoke 调用 Win32 函数并获得该用户所属的 Windows 组的列表。这些组用于填充 WindowsPrincipal 角色列表。
•WindowsAuthenticationModule 类将 WindowsPrincipal 对象存储在 HttpContext.User 属性中。随后,授权模块用它对经过身份验证的用户授权。
注:DefaultAuthenticationModule 类(也是 ASP.NET 管道的一部分)将 Thread.CurrentPrincipal 属性设置为与 HttpContext.User 属性相同的值。它在处理 AuthenticateRequest 事件之后进行此操作。
授权模块
WindowsAuthenticationModule 类完成其处理之后,如果未拒绝请求,则调用授权模块。授权模块也在计算机级别的 Web.config 文件中的httpModules 元素中定义,如下所示:
<httpModules> <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" /> <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" /> <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" /> </httpModules>
UrlAuthorizationModule
调用 UrlAuthorizationModule 类时,它在计算机级别或应用程序特定的 Web.config 文件中查找authorization 元素。如果存在该元素,则UrlAuthorizationModule 类从HttpContext.User 属性检索 IPrincipal 对象,然后使用指定的动词(GET、POST 等)来确定是否授权该用户访问请求的资源。
FileAuthorizationModule
接下来,调用 FileAuthorizationModule 类。它检查 HttpContext.User.Identity 属性中的IIdentity 对象是否是WindowsIdentity 类的一个实例。如果 IIdentity 对象不是 WindowsIdentity 类的一个实例,则 FileAuthorizationModule 类停止处理。
如果存在 WindowsIdentity 类的一个实例,则 FileAuthorizationModule 类调用AccessCheck Win32 函数(通过 P/Invoke)来确定是否授权经过身份验证的客户端访问请求的文件。如果该文件的安全描述符的随机访问控制列表 (DACL) 中至少包含一个Read 访问控制项 (ACE),则允许该请求继续。否则,FileAuthorizationModule 类调用HttpApplication.CompleteRequest 方法并将状态码 401 返回到客户端。
安全上下文
.NET Framework 使用以下两个接口封装 Windows 令牌和登录会话:
• | System.Security.Principal.IPrincipal |
• | System.Security.Principal.IIdentity(它公开为 IPrincipal 接口中的一个属性。) |
HttpContext.User
在 ASP.NET 中,用 WindowsPrincipal 和 WindowsIdentity 类表示使用 Windows 身份验证进行身份验证的用户的安全上下文。使用 Windows 身份验证的 ASP.NET 应用程序可以通过HttpContext.User 属性访问WindowsPrincipal 类。
要检索启动当前请求的 Windows 经过身份验证的用户的安全上下文,使用以下代码:
using System.Security.Principal; ... // Obtain the authenticated user's Identity WindowsPrincipal winPrincipal = (WindowsPrincipal)HttpContext.Current.User;
WindowsIdentity.GetCurrent
WindowsIdentity.GetCurrent 方法可用于获得当前运行的 Win32 线程的安全上下文的标识。如果不使用模拟,线程继承 IIS 6.0(默认情况下的 NetworkService 帐户)上进程的安全上下文。
该安全上下文在访问本地资源时使用。通过使用经过身份验证的初始用户的安全上下文或使用固定标识,您可以使用模拟重写该安全上下文。
要检索运行应用程序的安全上下文,使用以下代码:
using System.Security.Principal; ... // Obtain the authenticated user's identity. WindowsIdentity winId = WindowsIdentity.GetCurrent(); WindowsPrincipal winPrincipal = new WindowsPrincipal(winId);
Thread.CurrentPrincipal
ASP.NET 应用程序中的每个线程公开一个 CurrentPrincipal 对象,该对象保存经过身份验证的初始用户的安全上下文。该安全上下文可用于基于角色的授权。
要检索线程的当前原则,使用以下代码:
using System.Security.Principal; ... // Obtain the authenticated user's identity WindowsPrincipal winPrincipal = (WindowsPrincipal) Thread.CurrentPrincipal();
表 1 显示从各种标识属性获得的结果标识,当您的应用程序使用 Windows 身份验证且 IIS 配置为使用集成 Windows 身份验证时,可以从 ASP.NET 应用程序使用这些标识属性。
表 1:线程公开的 CurrentPrincipal Object | ||
Web.config 设置 | 变量位置 | 结果标识 |
<identity impersonate="true"/> | HttpContext | Domain\UserName |
<identity impersonate="false"/> | HttpContext | Domain\UserName |
<identity impersonate="true"/> | HttpContext | 用户提供的名称 |
<identity impersonate="false"/> | HttpContext |