|
在ASP.NET中实现Url Rewriting-2
1.7. 创建并注册自定义HttpModule和HttpHandler 创建自定义HttpModule的工作相对较简单,它包括一个实现当前接口的托管类,HttpModule必须实现System.Web.IHttpModule接口,同样HttpHandler和HttpHandlerFactory必须分别实现System.Web.IHttpHandler接口和System.Web.IhttpHandlerFactory接口。有关创建HttpHandler和HttpModule的细节已经超出本书范围,要了解更多详情请参阅Mansoor Ahmed Siddiqui的文章《HttpHandlers and HTTP modules in ASP.NET》(ASP.NET中的HttpHandler和HttpModule)。 一旦HttpModule和HttpHandler被创建后,必须向Web应用程序注册。如果要向整个Web服务器HttpModule和HttpHandler只需简单的写入machine.config文件;如果是由指定的Web应用程序调用则需在该程序的web.config配置文件中添加几行XML标记。 例如,要向指定的Web应用程序注册HttpModule和HttpHandler,只需向该Web应程序的web.config配置文件中configuration\System.Web节中添加下列几行:
<HttpModules>
<add type="type" name="name" />
</HttpModules>
其中type属性为HttpModule的标识号和类库名称,name属性则为该模块取一个较为友好的名称方便在Global.asax调用。 HttpHandler和HttpHandlerFactory则是在web.config文件中configuration\System.Web节中添加<httpHandler>标记,例如:
<httpHandlers>
<add verb="verb" path="path" type="type" />
</HttpModules>
回忆上文,ASP.NET对每一个接收到的请求指派相应的HttpHandler来处理并呈现相应内容,该指派决定于所接收请求的verb和path的内容,verb为HTTP请求的类型:GET或者POST,path则为请求的文件的路径和文件名。如果我们打算用一个HttpHandler来处理所有GET类型和POST类型的并且文件扩展名为.scott的内容,可以在web.config相应配置节中加入下列标记:
<httpHandlers>
<add varb="*" path=".scott" type="type" />
</httpHandlers>
其中type是我们定义的HttpHandler的类型。 注意:在注册HttpHandler的时候必须注意HttpHandler所使用的文件扩展名必须已经在IIS中做指向ASP.NET引擎的映射,在上面.scott扩展名的例子中,如果我们所使用的.scott扩展名如果没有在IIS中做指向ASP.NET引擎的映射的话,假定对foo.scott文件发出请求,该请求将导致IIS将foo.scott文件内容直接呈现给客户端,为了能够让HttpHandler处理该请求,必须将.scott扩展名在IIS中做指向ASP.NET引擎的映射,之后IIS才能正确地将.scott的请求转交给相应的HttpHandler。 有关HttpModule和HttpHandler更详细的内容请参阅MSDN中<HttpModules>节和<httpHandlers>节的文档信息。 <HttpModules>文档参考; <httpHandlers>文档参考。
1.8. 实现网址重写 网址重写技术不但可以在IIS Web服务器一级通过ISAPI过滤器实现,而且还可以在ASP.NET一级通过HttpModule或者HttpHandler实现。本文主要关注在ASP.NET一级实现网址重写技术,所以此时不必关注在ISAPI应用程序中实现网址重写的技术细节,而且有很多第三方厂商提供的ISAPI过滤器,比如 Helicon的ISAPI Rewrite; QwerkSoft的IIS Rewrite;Port80的PageXChanger; 等等。
1.9. 构建网址重写引擎 在ASP.NET中实现网址重写很简单,只需调用System.Web.HttpContext类的RewritePath()方法即可。HttpContext类中包含有关于特定HTTP请求的HTTP规范信息。ASP.NET引擎每接收到一个特定请求后便针对该请求创建一个特定的实例,这个类包含一些属性诸如:Request和Response属性,分别提供对请求和响应的访问;Application和Session属性提供对Application变量和Session变量的访问;User属性提供对已授权用户信息的访问。 在微软.NET Framework 1.0版本中,RewritePath()方法接收一个新路径的简单字符串,在其内部HttpContext类的RewritePath(string)方法内在地更新Request对象的路径和查询参数。除了RewritePath(string)方法之外,.NET Framework 1.1版还提供了另外一些重载版本,其中一个重载版本接收三个输入字符串参数,这种交替的重载形式不仅仅只是设置Request对象的路径和查询参数这些属性,而是设置更深层的成员变量,这些成员变量用于为PhysicalPath、PathInfo、FilePath属性计算Request对象值。 为了实现ASP.NET中的网址重写,我们需要创建一个HttpHandler和HttpModule用于: 根据请求的路径决定所需要重写的路径; 重写路径,如果需要的话可以调用RewritePath方法; 以前文所构建的那个站点为例,可以通过/info/employee.aspx?empID=EmployeeID来访问每一个雇员的信息。为了使这个网址更加地具有“隐蔽性”,我们可能会使用更加容易理解的访问方式如:/people/雇员名.aspx。这里就有了一个网址重写的案例:当接收到对/people/ScottMitchell.aspx的请求的时候,我们就得使用网址重写使得对该页面的请求被重写指向到先前使用的/info/employee?EmpID=1001地址。
1.10. 使用HttpModule来调用网址重写 在ASP.NET一级来执行网址重写,既可以使用HttpHandler,也可以使用HttpModule。当使用HttpModule的时候,必须决定如果该网址需要被重写的话,究竟应该在整个请求的生命周期期间的那一个点来使用。乍一看着有些武断,但是这个决定以重大而且微妙的方式影响到你的应用程序。之所以作出对网址重写点的选择是因为内嵌的ASP.NET HttpModule使用Request对象的属性值来完成自己的工作(回忆一下重写路径对Request对象的属性值的改变),这些内嵌HttpModule和相应事件的密切关系列举如下:
| HttpModule |
事件 |
简介 |
| FormsAuthenticationModule |
AuthenticateRequest |
判断用户是否已通过表单授权方式获取授权,如果没有的话则将用户重定向到指定的登录页面 |
| FileAuthorizationModule |
AuthorizeRequest |
当使用Windows授权方式的时候,该HttpModule判断并确定该Microsoft Windows帐户是否对其请求的资源拥有足够的权限 |
| UrlAuthorizationModule |
AuthorizeRequest |
检查并确认请求者是否对所访问的网址拥有权限。该Url授权可以在web.config文件的<authorization>和<location>元素中配置 | 回想一下BeginRequest事件在AuthenticateRequest事件之前引发,而AuthenticateRequest事件又在AuthorizeRequest事件之前引发。 实现网址重写的一个较为安全的场合就是把它放在在BeginRequest事件中执行,这意味着如果要执行网址重写的话,在众多内嵌HttpModule运行的时候他已经完成了。这种途径的最终用途淋漓尽致地体现在表单验证上。当用户访问受限资源的时候,如果之前使用了表单验证,他会自动被重定向到指定的登录页面,在成功登录之后,用户被重定向回先前试图访问的受限制页面。 如果把网址重写放在BeginRequest事件或者AuthenticateRequest事件中,在登录页面上执行提交后,该页面会将用户重定向到网址重写指定的页面。假定当用户在浏览器上敲入/people/ScottMitchell.aspx地址,该地址是要被重定向到/info/employee.aspx?EmpID=1001的,如果该Web应用程序设定使用表单验证,当用户开始访问/people/ScottMitchell.aspx的时候,该网址将重写指向/info/employee.aspx?EmpID=1001,接着ForumAuthenticationModule启动,如果需要的话将用户重定向到登录页面,用户登录后重定向到的页面将是/info/employee.aspx?EmpID=1001,这也是自从FormAuthenticationModule启动运行时所发出请求的页面。 同上类似,当把网址重写放在BeginRequest事件或者AuthenticateRequest事件中运行的时候,UrlAuthenticationModule也发现了网址重写指向的网址,这意味着如果在该应用程序的web.config文件中<location>节为特定的网址配置特定的授权地址的话,你得引用重写所指向的网址。 为了解决这个微妙的问题,一个可能就是把网址重写放在AuthorizeRequest事件中运行,但是在使用这种方法解决URL授权和表单授权的异常时又引入了一个新的缺陷:文件授权会失效。当使用Windows验证的时候,FileAuthorizationModule检查并验证已通过验证的用户是否拥有足够的权限访问特定的ASP.NET页面。 假定有一群用户并没有Windows级别的访问权限访问C:\inetpub\wwwroot\info\employee.aspx,当这些用户试图访问/info/employee.aspx?EmpID=1001的时候,他们会得到未授权的错误,如果我们把网址重写放到AuthenticateRequest事件中运行,当FileAuthorizationModule验证该安全性设置的时候,他仍任人为被请求的文件是/people/ScottMitchell.aspx,而这时该网址已经被重写了,因此FileAuthorizationModule会直接放行,让用户看到了网址重写指向的内容:/info/employee.aspx?Empid=1001。 那么什么时候在HttpModule调用网址重写合适呢?他决定于所使用的验证方式,当然如果不使用验证方式的话,那么无论是在BeginRequest事件、AuthenticateRequest事件还是AuthorizeRequest事件中调用网址重写没有多大区别,如果使用表单验证方式并且不使用Windows验证方式的话,把网址重写放入AuthorizeRequest事件委托中调用既可,如果使用Windows验证方式的话,把这项功能放入BeginRequest事件或者AuthenticateRequest事件调用就行了。
1.11. 使用HttpHandler来调用网址重写 除了上面所述方法外,网址重写也可以放入HttpHandler或者HttpHandlerFactory中调用。HttpHandler是一个负责针对特定请求生成相应内容的类,而HttpHandlerFactory返回一个HTTP的实例,该实例针对特定请求生成相应内容。 本节将着眼于为这些ASP.NET页面创建一个网址重写的HttpHandlerFactory。创建HttpHandlerFactory必须实现IHTTPHandlerFactory接口,它包括一个GetHandler()方法。ASP.NET引擎在初始化这些HttpModule后做出决定针对该请求调用相应的HttpHandler或者HttpHandlerFactory,在调用HttpHandlerFactory的时候,针对该Web请求以及随同的其他信息的HttpContext中经过的的HttpHandlerFactory的GetHandler()方法将被ASP.NET引擎调用,HttpHandlerFactory必须返回一个能委托该请求的对象,并且该对象要能实现IHttpHandler接口。 要通过一个HttpHandler来调用网址重写,可以先创建一个HttpHandlerFactory,它的GetHandler()方法检查所请求的网址并决定是否需要调用网址重写。如果要调用网址重写的话则调用前文所述的已通过检查的HttpContext对象的RewritePath()方法。最后该HttpHandlerFactory返回一个由类System.Web.UI.PageParser的GetCompiledInstance()方法返回的HttpHandler。(这与内嵌于ASP.NET页面的HttpHandlerFactory(PageHandlerFactory)的工作原理相同。) 在所有HttpModule被初始化后,HttpHandlerFactory就开始被实例化。把网址重写放在这些事件场所的最后一个里头调用的时候,也会碰到相同的问题:文件授权将会失效。如果非要依赖于Windows验证和文件验证的时候,你可能得使用HttpModule来调用网址重写了。 下一章我们着眼于如何构建一个可重用的网址重写引擎,使用下文所提的这些示例均以真实案例作为参照,在作者主页上提供下载。先用用一个简单的网址重写的例子来探讨如何实现网址重写,紧接着将利用网址重写引擎中正则表达式的强大处理能力来展示真正“隐蔽”的网址重写技术! 2006-9-5 9:48:54
Posted by jser | 阅读全文() | 回复(2) | 引用通告() | 编辑
|