HttpModule 示例
—— 一點一點學ASP.Net
文野:
概述
從前面的章節我們知道HTTP Handler提供了類似於ISAPI Server Extention的功能,而HttpModule實現了類似於ISAPI Filter的功能。使用自定義的Handler會覆蓋系統默認的Handler,而Module是可以多個同時存在的。
HttpHandler與HttpModule簡單來說其實都是對一個請求上下文的處理,但它們兩個所處的功能角色是完全不一樣的。我暫時還想不出一個既簡單又能體現HttpHandler優點的場景來,其實Page類就是一個實現了IHttpHandler的類,在AJax中也有相關的實現,以及ASP.Net Forums V2中有兩個比較簡單的實現(AvatarHttpHandler和VCardHttpHandler),大家可以去參考一下。
下面是一個檢查用戶是否登錄及模塊授權的HttpModule示例。
場景
一個網站,有一個首頁(Default.ASPx),一個登錄頁面(Login.ASPx),兩個模塊(模塊1和模塊2)。
一、 當用戶在未登錄的情況下訪問網站的任一個頁面都會跳轉到登錄頁面要求用戶登錄,登錄完成後跳轉到網站首頁並在每個頁面上顯示歡迎詞。
二、 假設有兩個用戶,一個“文野”,一個“stwyhm”,文野可以訪問模塊1,stwyhm可以訪問模塊2,當他們訪問各自有權訪問的模塊時,顯示模塊給出的歡迎詞,如果訪問的模塊沒有訪問權限,給出錯誤提示。其它用戶只能訪問指定模塊以後的頁面。
示例
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
namespace AuthorizationModule
{
/// <summary>
/// 說明:檢查用戶登錄的Module
/// 作者:文野
/// 聯系:stwyhm.cnblogs.com
/// </summary>
public class UserAuthorizationModule : IHttpModule
{
#region IHttpModule 成員
public void Dispose()
{ }
public void Init(HttpApplication context)
{
context.AcquireRequestState += newEventHandler(context_AcquireRequestState);
}
void context_AcquireRequestState(object sender, EventArgse)
{
// 獲取應用程序
HttpApplication application = (HttpApplication)sender;
// 檢查用戶是否已經登錄
if (application.Context.Session["UserName"] == null || application.Context.Session["UserName"].ToString().Trim() == "")
{
// 獲取Url
string requestUrl = application.Request.Url.ToString();
string requestPage = requestUrl.Substring(requestUrl.LastIndexOf('/') + 1);
// 如果請求的頁面不是登錄頁面,剛重定向到登錄頁面。
if (requestPage != "Login.ASPx")
application.Server.Transfer("Login.ASPx");
}
else
{
// 已經登錄,向每個請求的頁面打印歡迎詞。
application.Response.Write(string.Format("歡迎您!{0}!", application.Context.Session["UserName"]));
}
}
#endregion
}
/// <summary>
/// 說明:檢查用戶是否有權使用模塊的Module
/// 作者:文野
/// 聯系:stwyhm.cnblogs.com
/// </summary>
public class SystemModuleAuthorizationModule : IHttpModule
{
#region IHttpModule 成員
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AcquireRequestState += newEventHandler(context_AcquireRequestState);
}
void context_AcquireRequestState(object sender, EventArgse)
{
HttpApplication application = (HttpApplication)sender;
// 如果用戶未登錄,則無需檢查模塊授權,因為請求會被用戶登錄Module重定向到登錄頁面。
if (application.Session["UserName"] == null)
return;
// 獲取用戶名和Url
string userName = application.Session["UserName"].ToString();
string url = application.Request.Url.ToString();
// 如果用戶沒有被授權,請求被終止,並打印提示信息。
if (!Validator.CanUseModule(userName, url))
{
application.CompleteRequest();
application.Response.Write(string.Format("對不起!{0},您無權訪問此模塊!", userName));
}
}
#endregion
}
public class Validator
{
/// <summary>
/// 檢查用戶是否被授權使用模塊。
/// 文野可以使用模塊,stwyhm可以使用模塊,所有用戶都可以請求限定模塊以外的頁面
/// </summary>
/// <param name="userName"></param>
/// <param name="url"></param>
/// <returns></returns>
public static bool CanUseModule(string userName, stringurl)
{
if (!url.Contains("模塊"))
return true;
else if (userName == "文野" && url.Contains("模塊1"))
return true;
else if (userName == "stwyhm" && url.Contains("模塊2"))
return true;
else
return false;
}
}
}
頁面中除了簡單的登錄保存用戶名到Session的代碼及一些Html呈現代碼外無任何代碼。
執行結果
第一步:打開網站首頁,因示登錄被跳轉到登錄頁面
第二步:登錄成功後跳轉到首面。
第三步:訪問有權進入的模塊1
第四步:訪問無權進入的模塊2
開發注意點
無論是通過Url發出請求,還是一個按鈕引起的頁面回發,對IIS來說是一樣的,都是一次請求。而HttpModule中的事件一般都在頁面事件前,特別是控件事件,所以如果在HttpModule的事件中對請求進行過濾處理後就不會執行到頁面事件或控件事件,這就是上面示例的UserAuthorizationModule代碼中為什麼要對請求頁面是否是Login.ASPx進行判斷的原因了。
總結
這裡兩個自定義的HttpModule實現了它們各自所要達到的過濾請求的功能,一個限制用戶登錄,一個限制模塊訪問,當然實際應用中比這要復雜許多。這樣的驗證方式是簡單的、安全的,代碼在修改時只要修改相應的HttpModule就可以了,無需在每個頁面都寫相同的驗證代碼,也不會發生在Url地址欄裡輸入一段Url就可以跳過登錄及其它驗證的情況了。在這兩個HttpModule中,因為都要涉及到對Session的訪問,所有都使用了AcquireRequestState事件,大家可以根據實際情況使用不同的事件,可以參考我前面文章中的那副HttpModule生命周期圖。