views:

180

answers:

3

I have an intermediary class extending System.Web.UI.Page for all of my pages that require authentication. The class mostly does custom authentication handling.

When a user with insufficient access attempts to visit a page, I try to redirect the user back to the login page while preventing any further page events from being executed (ie. Page_load). The first solution that came to mind was the default implementation of Response.Redirect. Of course the downside to this is the possibility of ThreadAbortExceptions being thrown.

So my question is this: When (if at all) during the page life cycle is it actually safe to execute Response.Redirect() without ThreadAbortException ever being thrown?

public class CustomPage : System.Web.UI.Page
{
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        if (!IsValid())
            Response.Redirect("login.aspx", true);
    }
}
+1  A: 

Curious, why are you doing this yourself? If anything, you should be using one of the authentication providers (ultimately, FormsAuthentication can be customized to handle almost any scenario you can think of).

Then, you can use the authorization element in your web.config file to indicate what pages/directories are not able to be accessed by anonymous users. ASP.NET will take care of the rest, redirecting the user to the login page you specify, as well as redirecting back when the user has logged in.

casperOne
That was one option I had considered. I thought this was the easiest and most easily maintained of the solutions I came up with. All of the pages requiring authentication are kept in a single directory. There are 5+ roles for accessing the pages and each page in the directory (20+) have different role requirements. I saw myself doing up a location section in the web.config for each page requiring authentication. My other option was to split the files into separate directories for each role. The issue I had with this was path maintenance if a user was in multiple roles.
DDechant
+3  A: 

It's never "safe" if you're passing true as the second parameter - it will always throw the exception. Internally, Response.Redirect() calls Response.End(), which directly aborts the current thread.

The only "safe" way to truncate an HttpRequest without having an exception thrown is by using HttpApplication.CompleteRequest(), but this will result in further code execution in the current request.

womp
I'm not sure this is the case. The reason I think otherwise is because Response.End() checks for IsInCancellablePeriod before throwing the exception. So somehow, somewhere Response.End() can be called without hitting that exception.
DDechant
You'll need to dig deeper. You'll see that IsInCancellablePeriod is entirely controlled by a flag on HttpApplication.IExecutionStep interface objects. This flag is defined as `return !(this._application.Context.Handler is IHttpAsyncHandler);` for any CallHandler implementation (which is what calls ProcessRequest, which is what invokes the page lifecycle). In other words, unless you are executing an asynchronous request, it will always be false for the entire page lifecycle.
womp
So if I call Response.Redirect("login.aspx") without a try/catch shouldn't it hit HttpApplication.Application_Error (which it isn't)?
DDechant
I think it should, although I could be wrong. ThreadAbortException is a pretty special case exception.
womp
A: 

If you don't want a ThreadAbort exception you should pass False to the endResponse parameter. Of course this means you have to process the rest of the page, which is hard to get right.

Unless you are doing something really stupid like holding a lock, it is perfectly safe to throw a ThreadAbort exception in an ASP.NET page.

Another option is to use a Server.Transfer. This has better performance than a redirect, but it too uses ThreadAbort exceptions.

Jonathan Allen