Authorize HttpModule (with Session)
See also PreInitModule.
Alternative using IHttpHandler
Scenario: Control access to a resource using business rules (eg a session flag, SessionManager.IsSubscribed())
- Secure Asp page/resource: use context.AuthorizeRequest (if no Session) or PreRequestHandlerExecute (if Session). details
- Secure pdfs, zips, images: Ad a wildcard mapping to IIS so the resource is mapped to the asp ISAPI
Warning: Unfortunately you can't add an HttpModule just for a subfolder, so this module runs for the entire website and checks IfRestrictedFolder (comparing an appSetting with the request.Path).
- At PostMapRequestHandler the HttpModule swaps from the actual handler to a custom handler which implements IRequiresSessionState or IReadOnlySessionState so it can use seesion.
- At PreRequestHandlerExecute you can access session and redirect if needed.
- At PostAcquireRequestState you swap back to the orginal handler.
In practice, writing session is unreliable (asp writes the session cookie, but the handler may response.Clear). Also session writing takes out a lock, and Phil found that a deadlock could occur with this module using IRequiresSessionState. In conclusion- just use IReadOnlySessionState!
Credit to Tomasz Jastrzebski http://forums.asp.net/p/1098574/1664675.aspx
using System;
using System.Web;
using System.Web.Configuration;
using System.Web.SessionState;
/// <summary>
/// HttpModule to verify that only subscribers can view resources.
/// Because of an asp buglet, HttpModules and subfolders (web.config location) doesn't work
/// so the authorization stuff uses an appsetting to check Request.Path for the subfolder.
/// </summary>
public class SubscriptionModule : IHttpModule
{
#region IHttpModule Members
///<summary>
///Initializes a module and prepares it to handle requests.
///</summary>
///<param name="context">An <see cref="T:System.Web.HttpApplication"></see> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application </param>
public void Init(HttpApplication context)
{
//if the handler doesn't have session, swap in a temp handler that does have session
context.PostMapRequestHandler += PostMapRequestHandler;
//swap out the temp handler if we used it
context.PostAcquireRequestState += PostAcquireRequestState;
//the authorization code, using session
context.PreRequestHandlerExecute += context_PreRequestHandlerExecute;
}
private static void PostMapRequestHandler(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
//don't care about images
if (!IsRestrictedFolder(context.Request)) return;
// no need to replace the current handler
if (context.Handler is IReadOnlySessionState || context.Handler is IRequiresSessionState)
return;
// swap the current handler
context.Handler = new MyHttpHandler(context.Handler);
}
private static void PostAcquireRequestState(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
MyHttpHandler resourceHttpHandler = context.Handler as MyHttpHandler;
if (resourceHttpHandler != null)
// set the original handler back
context.Handler = resourceHttpHandler.OriginalHandler;
}
static void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
if (!IsRestrictedFolder(context.Request)) return;
//if they are not subscribed, redirect to login
if (!SessionManager.IsSubscribed())
context.Response.Redirect("~/login.aspx");
}
private static bool IsRestrictedFolder(HttpRequest request)
{
//if Request Path subfolder (after application path) is our restricted folder
string dir = string.Format("{0}/{1}", request.ApplicationPath, WebConfigurationManager.AppSettings["subFolders"]);
return request.Path.StartsWith(dir, StringComparison.OrdinalIgnoreCase);
}
///<summary>
///Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"></see>.
///</summary>
public void Dispose()
{
}
#endregion
/// <summary>
/// Temp handler used to force the SessionStateModule to load session state.
/// From Tomasz Jastrzebski http://forums.asp.net/p/1098574/1664675.aspx
/// </summary>
public class MyHttpHandler : IHttpHandler, IReadOnlySessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable // IsReusable must be set to false since class has a member!
{
get { return false; }
}
}
}