IIS 和 ASP.NET 处理
注意:本节中的信息适用于在 Windows 2000 上运行的 Internet 信息服务 (IIS) 5。
ASP.NET Web 应用程序和 Web 服务是由在单个 ASP.NET 辅助进程 (aspnet_wp.exe) 实例中执行的代码处理的,但是,可以在多处理器计算机上配置多个实例(每个处理器一个)。
IIS 验证调用方的身份,并为调用方创建 Windows 访问令牌。如果在 IIS 中启用了匿名访问,则 IIS 为匿名 Internet 用户帐户(通常是 IUSR_MACHINE)创建 Windows 访问令牌。
ASP.NET 文件类型请求是由 ASP.NET ISAPI 扩展 (aspnet_isapi.dll) 处理的,该扩展在 IIS (inetinfo.exe) 进程地址空间中运行。该扩展使用命名管道与 ASP.NET 辅助进程进行通信,如图 1 所示。IIS 将代表调用方的 Windows 访问令牌传递给 ASP.NET 辅助进程。ASP.NET Windows 身份验证模块使用此令牌构造一个 WindowsPrincipal 对象;而 ASP.NET 文件授权模块使用它来执行 Windows 访问检查,以确保授权调用方访问请求的文件。
图 1
IIS 和 ASP.NET 通信
注意:访问令牌与进程有关。因此,在 inetinfo.exe 中运行的 ASP.NET ISAPI DLL 调用 DuplicateHandle 以便将令牌句柄复制到 aspnet_wp.exe 进程地址空间中,然后通过命名管道传递句柄值。
应用程序隔离
可以使用辅助进程内的不同应用程序域(每个 IIS 虚拟目录一个,换言之,每个 ASP.NET Web 应用程序或 Web 服务一个)来提供隔离。
它与传统的 ASP 不同。在传统的 ASP 中,IIS 元数据库中配置的应用程序保护级别决定了 ASP 应用程序是在 IIS 进程内 (inetinfo.exe) 执行,在进程外的 Dllhost.exe 专用实例中执行,还是在 Dllhost.exe 的共享(汇集)实例中执行。
重要说明:IIS 中的进程隔离级别设置不影响 ASP.NET Web 应用程序的处理方式。
ASP.NET ISAPI 扩展
ASP.NET ISAPI 扩展 (aspnet_isapi.dll) 在 IIS 进程地址空间 (inetinfo.exe) 中运行,并通过命名管道将 ASP.NET 文件类型请求转发给 ASP.NET 辅助进程。
可以通过 IIS 元数据库中定义的映射,将特定的 ASP.NET 文件类型映射到 ASP.NET ISAPI 扩展。在安装 .NET Framework 后,就会建立标准 ASP.NET 文件类型(包括 .aspx、.asmx、.rem 以及 .soap)的映射。
• | 查看应用程序映射
|
IIS 6.0 和 Windows .NET Server
Windows .NET Server 上的 IIS 6.0 对当前的进程设置进行了一些重大改动。
• | 您可以配置多个应用程序池,每个池由一个或多个进程实例 (w3wp.exe) 提供服务。这可提供额外的容错功能并简化了管理,使您可以在不同的进程中隔离不同的应用程序。 |
• | ASP.NET 与 IIS 6.0 内核模式 HTTP 侦听器集成在一起,因此,可以将请求从操作系统直接传递给 ASP.NET 辅助进程。 |
更多信息
有关 IIS6 的详细信息,请参见 TechNet 上的文章“IIS 6 Overview”(IIS 6 概述)(http://www.microsoft.com/technet/treeview/default.asp?url=/TechNet/prodtechnol/iis/evaluate/iis6ovw.asp)。
ASP.NET 管道处理
ASP.NET 身份验证和授权机制是使用 HTTP 模块对象实现的,HTTP 模块对象是作为标准 ASP.NET 管道处理的一部分进行调用的。各个 Web 请求和响应通过对象管道进行传递,如图 2 所示:
图 2
ASP.NET 管道处理
ASP.NET 管道模型包括 HttpApplication 对象、各种 HTTP 模块对象和 HTTP 处理程序对象及其相关工厂对象。为清楚起见,图 2 中省略了工厂对象。HttpRuntime 对象在处理序列开始时使用;而 HttpContext 对象在整个请求过程中使用,以传送有关请求和响应的详细信息。
以下列表说明了 HTTP 处理管道相关对象的职责和所执行的操作:
• | HttpRuntime 对象检查从 IIS 收到的请求,并将其分配给 HttpApplication 对象的相应实例以进行处理。Aspnet_wp.exe 的每个应用程序域中都有一个 HttpApplication 对象池。应用程序域、HttpApplication 对象和 IIS 虚拟目录之间存在一一对应关系。也就是说,ASP.NET 将不同的 IIS 虚拟目录作为不同的应用程序处理。 注意:每个 Web 应用程序域中都有一个 HttpRuntime 实例。 |
• | HttpApplication 对象控制管道处理。对于同时发生的每个 HTTP 请求,分别创建一个 HttpApplication 对象以便对它进行处理。出于性能方面的考虑,将 HttpApplication 对象汇集起来。 |
• | 在管道中传递 HTTP 请求和响应消息时,可以将 HTTP 模块对象作为处理它们的筛选器。这些模块对象可以查看或修改请求和响应消息的内容。HTTP 模块是实现 IHttpModule 的类。 |
• | HTTP 处理程序对象是 HTTP 请求的终结点,并对特定文件类型的请求进行处理。例如,一个处理程序处理对 *.aspx 文件的请求,而另一个处理程序则处理对 *.asmx 文件的请求。HTTP 响应消息是由 HTTP 处理程序生成和返回的。HTTP 处理程序是实现 IHttpHandler 的类。 |
• | HttpContext 对象在整个管道中使用以代表当前的 Web 请求和响应。它可用于管道中的所有模块以及管道结束处的处理程序对象。HttpContext 对象公开各种不同的属性,其中包括 User 属性,该属性包含代表调用方的 IPrincipal 对象。 |
Web 请求剖析
ASP.NET ISAPI 库 (Aspnet_isapi.dll) 在 IIS 进程地址空间 (Inetinfo.exe) 中运行。它将请求分配给 ASP.NET 辅助进程 (Aspnet_wp.exe) 中的 HttpRuntime 对象。对于 ASP.NET 收到的每个 Web 请求,将执行以下一系列操作以进行响应:
• | HttpRuntime 对象检查请求,并将其转发给 HttpApplication 对象的实例。 |
• | 从 Machine.config 中读取 HTTP 模块列表(它们包含在 <httpModules> 元素内)。可以在某个应用程序的 Web.config 中添加其他的自定义 HTTP 模块。以下代码片段显示了 Machine.config 中的默认 <httpModules> 元素: <httpModules> <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/> <add name="Session" type="System.Web.SessionState.SessionStateModule"/> <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule"/> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/> <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule"/> <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"/> <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule"/> </httpModules> 身份验证模块与 AuthenticateRequest 事件挂钩;而授权模块与 AuthorizeRequest 事件挂钩。 请求在传递过程中通过管道中的每一个模块,但只加载一个身份验证模块。这取决于 Web.config 中 <authentication> 元素的配置。例如,下面的 <authentication> 元素导致加载 WindowsAuthenticationModule: <authentication mode="Windows" /> |
• | 激活的身份验证模块负责创建 IPrincipal 对象,并将其存储在 HttpContext.User 属性中。这是非常重要的,因为下游授权模块使用此 IPrincipal 对象来确定是否授权。 在未进行身份验证的情况下(例如,在 IIS 中启用匿名访问并且将 ASP.NET 配置为 <authentication mode="None" />),可以使用一个特殊的不可配置模块,将默认的匿名主体放到 HttpContext.User 属性中。因此,在进行身份验证后,HttpContext.User 始终是非空的属性。 如果您实现自定义的身份验证模块,则自定义模块中的代码必须创建一个 IPrincipal 对象,并将其存储在 HttpContext.User 中。 注意:在 AuthenticateRequest 事件之后,ASP.NET 还根据 HttpContext.User 调用 Thread.CurrentPrincipal。 |
• | HttpApplication 激发 AuthenticateRequest 事件,该事件可以挂钩到 global.asax 中。这样,您就可以注入自定义的处理代码以加载与当前用户关联的角色集。但要注意,WindowsAuthenticationModule 自动执行此操作。角色列表是从已验证身份的 Windows 用户所属的 Windows 组集中获取的。 |
• | 在相应身份验证模块完成处理之后,如果请求未被终止,则调用授权模块。 |
• | 在调用 UrlAuthorizationModule 后,它检查 Machine.config 和 Web.config 中是否存在 <authorization> 标记。如果存在,则它从 HttpContext.User 中检索 IPrincipal 对象,并使用指定的谓词(GET、POST 等等)检查是否授权用户访问请求的资源。 |
• | 然后,调用 FileAuthorizationModule。它检查 HttpContext.User.Identity 中的IIdentity 对象是否为 WindowsIdentity 类的实例。 如果 WindowsIdentity 存在,则 FileAuthorizationModule 调用 AccessCheck API(通过 P/Invoke),查看是否授权已验证身份的调用方(IIS 已将其访问令牌传递给 ASP.NET 并由 WindowsIdentity 对象公开)访问请求的文件。如果文件的安全描述符在其 DACL 中至少包含一个读取 ACE,则可以对请求继续进行处理。否则,FileAuthorizationModule 调用 HttpApplication.CompleteRequest 并返回 401 状态代码。 |
窗体身份验证处理
如果 Web.config 包含以下元素,就会激活 FormsAuthenticationModule:
<authentication mode="Forms" />
记住,对于窗体身份验证,可以在 Global.asax 中实现 Application_Authenticate 事件。对于窗体身份验证,将发生以下事件序列:
• | 在此代码中,您可以构造 IPrincipal 对象并将其存储在 HttpContext.User 中。通常,它包含从自定义数据存储(通常是 SQL Server 数据库或 Active Directory)中检索的角色列表。IPrincipal 对象通常是 GenericPrincipal 类的实例,但也可以是自定义的 IPrincipal 类。 FormsAuthenticationModule 检查您是否创建了 IPrincipal 对象。如果已经创建,下游授权模块就会使用该对象。如果没有创建,FormsAuthenticationModule 就会构造 GenericPrincipal(没有角色)并将其存储在上下文中。 如果没有角色信息,则任何要求角色成员身份的授权检查(例如 PrincipalPermssion 请求)都将失败。 |
• | UrlAuthorizationModule 处理 AuthorizeRequest 事件。它的授权决策基于 HttpContext.User 中包含的 IPrincipal 对象。 |
Windows 身份验证处理
如果 Web.config 包含以下元素,就会激活 WindowsAuthenticationModule:
<authentication mode="Windows" />
对于 Windows 身份验证,将发生以下事件序列:
1. | WindowsAuthenticationModule 使用由 IIS 传递给 ASP.NET 的 Windows 访问令牌创建 WindowsPrincipal 对象。 |
2. | 它使用 P/Invoke 调用 Win32 函数以获取用户所属的 Windows 组的列表。这些组用于填充 WindowsPrincipal 角色列表。 |
3. | 它将 WindowsPrincipal 对象存储在 HttpContext.User 中,以便供下游授权模块使用。 |
事件处理
HttpApplication 对象激发一组事件,如表 1 所示。各个 HTTP 模块可以与这些事件挂钩(通过提供它们自己的事件处理程序)。
表 1:HttpApplication 对象激发的事件
事件 | 备注 |
BeginRequest | 在请求处理开始之前激发 |
AuthenticateRequest | 验证调用方的身份 |
AuthorizeRequest | 执行访问检查 |
ResolveRequestCache | 从缓存中获取响应 |
AcquireRequestState | 加载会话状态 |
PreRequestHandlerExecute | 在请求即将发送到处理程序对象之前激发 |
PostRequestHandlerExecute | 在请求刚刚发送到处理程序对象之后激发 |
ReleaseRequestState | 存储会话状态 |
UpdateRequestCache | 更新响应缓存 |
EndRequest | 在处理结束后激发 |
PreSendRequestHeaders | 在发送缓存的响应标头之前激发 |
PreSendRequestContent | 在发送缓存的响应正文之前激发 |
注意:HTTP 处理程序在 PreRequestHandlerExecute 和 PostRequestHandlerExecute 事件之间执行。
最后两个事件是无法确定的,并且可能会随时发生(例如,由 Response.Flush 激发)。所有其他事件则是按顺序发生的。
不必仅仅为了与其中的某个事件挂钩而实现 HTTP 模块。您还可以向 Global.asax 中添加事件处理程序。除了表 1 中列出的事件(它们可以与不同的 HTTP 模块对象挂钩)外,HttpApplication 对象还会激发 Application_OnStart 和 Application_OnEnd 处理程序,ASP 开发人员应该对这些处理程序很熟悉。只能在 Global.asax 中对这些处理程序进行处理。最后,您还可以在 Global.asax 中为不同 HTTP 模块对象激发的事件实现自定义的事件处理程序。例如,会话状态模块激发 Session_OnStart 和 Session_OnEnd 事件。
实现自定义的 HTTP 模块
• | 创建您自己的 HTTP 模块并将其插入到 ASP.NET 处理管道中
|
实现自定义的 HTTP 处理程序
您可能需要实现自定义的 HTTP 处理程序,以便处理具有 .data 文件扩展名的文件。
• | 实现自定义的 HTTP 处理程序
|