HttpModule的几个典型应用场景
2010-06-27 13:18 2323 阅读(596) 评论(0) 编辑 收藏 举报1.HttpModule概述
HttpModule是ASP.net开发中的瑞士军刀,如果适当地应用HttpModule可以提高系统的可维护性,HttpModule以极小的侵入性来解决系统中的具有通用性的问题。
HttpModule的典型应用场景有:
a. 用户验证
b. 权限控制
c. 用户访问控制,比如基于IP来控制用户对系统的访问
d. 脚本(CSS,JS等)的合并
e. URL Rewriting
f. 本地化(Localization)
g. 异常处理
h. 其他方面。
实际上,.net Framework中就大量采用了HttpModule.比如在.net 4.0的web.config文件中有如下的HttpModule.
<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="RoleManager" type="System.Web.Security.RoleManagerModule" /> <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" /> <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" /> <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" /> <add name="Profile" type="System.Web.Profile.ProfileModule" /> <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" /> <add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
这里仅仅展示用户验证,本地化和防止用户重复提交。
2.用户验证
通过判断Session中是否保存了UserName来判断用户是否已经登录系统。
用户验证的事件必须是在HttpApplication的AcquireRequestState及其以后的事件中处理,因为Session只有在AcquireRequestState及其以后的事件中才是可用的。
app.Context.Handler is IRequiresSessionState || app.Context.Handler is IReadOnlySessionState
对Css,js文件不需要进行验证,比如当Login.aspx引用了js文件的时候,那么即使用户没有登录,那么Login.aspx和js文件都是可以被访问到。
HttpMOdule的代码如下:
public class LoginHttpModule : IHttpModule { #region IHttpModule 成员 public void Dispose() { } public void Init(HttpApplication context) { context.AcquireRequestState += new EventHandler(loginCheck_PostAcquireRequestState); } #endregion private void loginCheck_PostAcquireRequestState(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; if (app.Context.Handler is IRequiresSessionState || app.Context.Handler is IReadOnlySessionState) { if (app.Context.Session["UserName"] == null && !app.Context.Request.RawUrl.EndsWith("Login.aspx")) app.Context.Response.Redirect("~/Login.aspx"); } } }
3.本地化,比如根据不同的国家设置不同的日期格式
/// <summary> /// 当然可可以在asp.net的页面中override InitializeCulture. /// </summary> public class LocalizationHttpModule : IHttpModule { #region IHttpModule 成员 public void Dispose() { } public void Init(HttpApplication context) { context.PreRequestHandlerExecute += new EventHandler(loc_PreRequestHandlerExecute); } #endregion private void loc_PreRequestHandlerExecute(object sender, EventArgs e) { //被注释代码仅仅处理Page //HttpApplication app = (HttpApplication)sender; //if (app.Context.Handler is Page) //{ // (app.Context.Handler as Page).PreRender += new EventHandler(PreRender); //} //可以处理Page,实现IHttpHandler,WebService中的国际化问题。 //法国的日期日期部分的格式 日/月/年 System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; } }
当然本地化也可以在asp.net的页面中override InitializeCulture中来处理.但是对于asmx类型的请求在InitializeCulture中是无法处理的。
4.防止用户重复提交
用户重复提交流程:
解决用户重复提交的流程如下:
HttpModule的代码主要是在页面上输出token.
代码如下:
public class TokenHttpModule : IHttpModule { #region IHttpModule 成员 public void Dispose() { } public void Init(HttpApplication context) { context.PostAcquireRequestState += new EventHandler(token_PostAcquireRequestState); } #endregion private void token_PostAcquireRequestState(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; //因为需要访问Session if (app.Context.Handler is Page) { (app.Context.Handler as Page).PreRenderComplete += new EventHandler(PreRenderComplete); } } private void PreRenderComplete(object sender, EventArgs arg) { Page page= (sender as Page); if (page == null) return; if (page.Form == null) return; HttpSessionState session=HttpContext.Current.Session; if (session[TokenUtil.TokenKey_InSessions] != null) { HiddenField ctrl = new HiddenField(); ctrl.ID = TokenUtil.TokenKey_InPages; ctrl.Value = session[TokenUtil.TokenKey_InSessions].ToString(); ctrl.ClientIDMode = ClientIDMode.Static; page.Form.Controls.Add(ctrl); } } }
TokenUtil的代码如下:
public static class TokenUtil { public const string TokenKey_InSessions="tokenkey"; public const string TokenKey_InPages = "Param_TokenKey"; public static bool IsValidToken() { HttpContext context = HttpContext.Current; HttpSessionState session = context.Session; if (session == null) return false; if (session[TokenKey_InSessions] == null) return false; if (String.IsNullOrWhiteSpace(context.Request[TokenKey_InPages])) return false; return session[TokenKey_InSessions].ToString().Equals(context.Request[TokenKey_InPages]); } public static void SaveToken() { HttpContext context = HttpContext.Current; HttpSessionState session = context.Session; if (session == null) return; session[TokenKey_InSessions] = Guid.NewGuid().ToString(); } public static void ResetToken() { HttpContext context = HttpContext.Current; HttpSessionState session = context.Session; if (session == null) return; session.Remove(TokenKey_InSessions); } }
编辑界面的代码如下:
public partial class PersonEdit : BasePage { protected void Page_Load(object sender, EventArgs e) { if (!this.IsPostBack) { base.SaveToken(); } } protected void Button1_Click(object sender, EventArgs e) { if (!base.IsValidToken()) { //Response.Redirect("InValidToken.aspx"); Label1.Text = "重复提交!!"; return; } int i = 0; PersonService ps = new PersonService(); ps.SavePerson(this.TextBox1.Text); base.ResetToken(); Response.Redirect("PersonList.aspx"); } }
代码文件下载地址.