IIS 7 with URL Rewrite Module 2.0 - setting 401 status codes and the ReturnUrl
I have a website hosted on IIS 7 with the URL Rewrite module 2.0 installed. It is run by a content management system that looks at the URL and returns a 401 error if the current user does not have permission to view the page. This gets picked up by the ASP.NET URL authorization module which then kicks the page over to the loginUrl page as specified in the web.config
file (forms authentication).
This works perfectly on my local machine - which is IIS 7 and Windows 7.
If the URL is, say, /612/some-string
the user gets directed to the login page at /66/login?ReturnUrl=/612/some-string
.
The URL rewriting looks at the first part of the URL for the document ID. The real URL would be this: index.aspx?documentId=612
Unfortunately, when I deployed this to our staging server, the ReturnUrl isn't the rewritten URL, it's the original URL. This causes all sorts of problems.
The staging server is also IIS 7 with the URL Rewrite Module 2.0 installed. It's Windows 2008 server SP2. Both are running ASP.NET 3.5.
My only guess is that the machine.config
file orders the default httpModules differently, and the .NET forms authentication module is jumping in before the URL has been rewritten.
I'll review that soon, but in the meantime, what is experience with this problem and can it be solved?
Update
I also tried changing
Response.StatusCode = 401;
to
FormsAuthentication.RedirectToLoginPage();
Which gets me a bit ahead开发者_开发技巧, but still directs the user back to the URL that hasn't been rewritten.
I can also do this instead of setting the 401:
string currentPage = HttpUtility.UrlEncode(Request.RawUrl);
string loginUrl = FormsAuthentication.LoginUrl + "?ReturnUrl=" + currentPage;
Response.Redirect(loginUrl);
But this seems ugly.
In chapter 2 of the book Developing More=Secure Microsoft ASP.NET 2.0 Applications by Dominick Baier there is a ShowPipeline.ashx which shows the complete pipeline ordering on the server using an HttpHandler:
<%@ WebHandler Class='ShowPipeline' Language='c#' %>
using System;
using System.Web;
using System.Reflection;
using System.ComponentModel;
// shows which modules have registered for which event
// add a ?asm=true query string parameter to also show the assemblies
public class ShowPipeline : IHttpHandler
{
static bool _showAssemblies = false;
// names of the pipeline events
static string[] _handlerNames = {
"BeginRequest",
"AuthenticateRequest",
"DefaultAuthentication",
"PostAuthenticateRequest",
"AuthorizeRequest",
"PostAuthorizeRequest",
"ResolveRequestCache",
"PostResolveRequestCache",
"AcquireRequestState",
"PostAcquireRequestState",
"PreRequestHandlerExecute",
"PostRequestHandlerExecute",
"ReleaseRequestState",
"UpdateRequestCache",
"PostUpdateRequestCache",
"EndRequest"
};
public void ProcessRequest(HttpContext ctx)
{
if (ctx.Request.QueryString["asm"] == "true")
_showAssemblies = true;
ctx.Response.Write("<hr>");
foreach (string s in _handlerNames)
{
_showHandlers(s);
}
ctx.Response.Write("<hr>");
}
public void _showHandlers(string handlerName)
{
HttpResponse r = HttpContext.Current.Response;
object key = _getPrivateAppField("Event" + handlerName);
EventHandlerList ehl = (EventHandlerList)_getPrivateAppField("_events");
MulticastDelegate md = (MulticastDelegate)ehl[key];
if (null != md)
{
r.Output.WriteLine("<h2>{0}</h2>", handlerName);
foreach (Delegate d in md.GetInvocationList())
{
Type tt = d.Target.GetType();
string asm = "";
if (_showAssemblies)
{
asm = string.Format("<font color='red'>[{0}]</font>", tt.Assembly.GetName());
}
r.Output.WriteLine("{0}{1}.<font color='blue'>{2}</font><br>", asm, tt, d.Method.Name);
}
}
}
object _getPrivateAppField(string fieldName)
{
return _getPrivateField(typeof(HttpApplication), fieldName, HttpContext.Current.ApplicationInstance);
}
object _getPrivateField(Type t, string fieldName, object o)
{
return t.GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic).GetValue(o);
}
object _getPrivateField(string fieldName, object o)
{
return o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic).GetValue(o);
}
public bool IsReusable { get { return true; } }
}
精彩评论