Redirecting to custom 401 page when "Access denied" occures within an ASP.NET application with Windows authentication
2008-11-12 15:35
519 查看
If you have an ASP.NET application with authentication mode set to Windows:
Then all Windows users can access your pages but those without Windows login (from internet) will receive an ASP.NET "Access Denied 401" page. I have decided to replace this default message with some custom page. After couple of hours Googling, I found out that this is a very common problem and all the workarounds which seem to be reasonable does not work. Here are some of them:
Use a custom error page in Web.config:
This works fine with
Try to catch an unauthorized request in one of the application events in Global.asax.cs:
It is also useless, because even the browsers with valid credentials make two posts, one without credentials and having
That's correct. There's no way around that. The way wininet authentication works is that if the resource you are requesting does not allow anonymous access, a 401 is sent back to the browser. If the resource is using Windows Integrated authentication and the browser is configured to automatically send credentials, the token is sent back and the user is authenticated. In the case of Basic authentication, a login prompt is displayed and the user must log in.
If you intercept the 401 and redirect somewhere, you hijack the browser's ability to challenge. There is no way around that.
Jxx Cxxxxxx, MCSE, MCSD [MSFT], Developer Support, ASP.NET.
The browser sends request without credentials to the server.
Server rejects this request and answers with "401 Access Denied".
Browser recognizes 401 and if it has appropriate credentials does not show this message. The second request with credentials will be posted. The requested page will be sent to the browser.
If the browser has no credentials the second post will not take place. The received "401 Access Denied" will be shown.
The workaround is to manipulate the content of "401 Access Denied" response. The browser uses the header of this response to determine 401 case. It means we can manipulate the HTML content without influencing the whole challenge. For instance we can add the following code in the
Now, the whole thing works like this:
The browser sends request without credentials to the server.
Server rejects this request and answers with "401 Access Denied" Header + our JavaScript.
Browser recognizes 401 and if it has appropriate credentials does not show this message. Our HTML will not be rendered and JavaScript will not be executed. The second request with credentials will be posted. The requested page will be sent to the browser.
If the browser has no credentials, the second post will not take place. The received "401 Access Denied" will be shown. Our HTML will be rendered and JavaScript will be executed. The client side redirection will take place. The browser will show a custom 401 page.
<authentication mode="Windows"/> <authorization> <deny users="?" /> </authorization>
Then all Windows users can access your pages but those without Windows login (from internet) will receive an ASP.NET "Access Denied 401" page. I have decided to replace this default message with some custom page. After couple of hours Googling, I found out that this is a very common problem and all the workarounds which seem to be reasonable does not work. Here are some of them:
Use a custom error page in Web.config:
<customErrors defaultRedirect="ErrorPage.aspx" mode="On"> <error statusCode="401" redirect="AccessDenied.aspx" /> </customErrors>
This works fine with
statusCode="404"but not with 401.
Try to catch an unauthorized request in one of the application events in Global.asax.cs:
protected void Application_AuthenticateRequest(Object sender, EventArgs e) { if (!Request.IsAuthenticated) Response.Redirect( "AccessDenied.aspx"); }
It is also useless, because even the browsers with valid credentials make two posts, one without credentials and having
Request.IsAuthenticated=falseand the other one with credentials and having
Request.IsAuthenticated=true. Browsers without credentials make only one post. It means you are not able to determine if the first post having
Request.IsAuthenticated=falsecomes from an unauthorized browser or not.
The problem
Then I found an answer from a Guru at microsoft.public.dotnet.framework.aspnet.security newsgroup.That's correct. There's no way around that. The way wininet authentication works is that if the resource you are requesting does not allow anonymous access, a 401 is sent back to the browser. If the resource is using Windows Integrated authentication and the browser is configured to automatically send credentials, the token is sent back and the user is authenticated. In the case of Basic authentication, a login prompt is displayed and the user must log in.
If you intercept the 401 and redirect somewhere, you hijack the browser's ability to challenge. There is no way around that.
Jxx Cxxxxxx, MCSE, MCSD [MSFT], Developer Support, ASP.NET.
Resolution
That was also the result of my research. The whole thing works like this:The browser sends request without credentials to the server.
Server rejects this request and answers with "401 Access Denied".
Browser recognizes 401 and if it has appropriate credentials does not show this message. The second request with credentials will be posted. The requested page will be sent to the browser.
If the browser has no credentials the second post will not take place. The received "401 Access Denied" will be shown.
The workaround is to manipulate the content of "401 Access Denied" response. The browser uses the header of this response to determine 401 case. It means we can manipulate the HTML content without influencing the whole challenge. For instance we can add the following code in the
Application_EndRequestevent of Global.asax.cs.
protected void Application_EndRequest(Object sender, EventArgs e) { HttpContext context = HttpContext.Current; if (context.Response.Status.Substring(0,3).Equals("401")) { context.Response.ClearContent(); context.Response.Write("<script language="javascript">" + "self.location='../login.aspx';</script>"); } }
Now, the whole thing works like this:
The browser sends request without credentials to the server.
Server rejects this request and answers with "401 Access Denied" Header + our JavaScript.
Browser recognizes 401 and if it has appropriate credentials does not show this message. Our HTML will not be rendered and JavaScript will not be executed. The second request with credentials will be posted. The requested page will be sent to the browser.
If the browser has no credentials, the second post will not take place. The received "401 Access Denied" will be shown. Our HTML will be rendered and JavaScript will be executed. The client side redirection will take place. The browser will show a custom 401 page.
相关文章推荐
- How to integrate custom security policy with Windows domain authentication in ASP.NET
- A frame in a frameset may become blank on an ASP page or in an ASP.NET application(frameset 可能变成空白)
- ASP.NET Ver 1.1 Web Application and Windows Authentication – a Case Study
- An ASP.NET Gridview Control With Custom Paging (Technical)
- ASP.NET 解决An attempt was made to load a program with an incorrect format.问题
- Sending e-mail with attachments from an ASP.NET page
- Introduction: Connect to SQL Membership with ASP.NET Application
- How to set an IIS Application or AppPool to use ASP.NET 3.5 rather than 2.0
- Tip/Trick: ASP.NET 2.0 Deal with DBNull value when bind to RadioButtonList/当绑定RadioButtonL
- Passing parameters to an ASP.NET page running in an IFRAME
- How to enable an ASP.Net application to run on a SharePoint virtual server
- ASP.Net DebugError解决方案[转]:Unable to start debugging on the web server.Debugging failes because integrated Windows authentication is not enabled.
- Sorting, Filtering, and Paging with the Entity Framework in an ASP.NET MVC Application
- ASP.NET: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. on Win7 64bit
- How to set an IIS Application or AppPool to use ASP.NET 3.5 rather than 2.0
- [转]Sorting, Filtering, and Paging with the Entity Framework in an ASP.NET MVC Application (3 of 10)
- Tip/Trick: ASP.NET 2.0 Deal with DBNull value when bind to RadioButtonList/当绑定RadioButtonL
- How to get URL and QueryString value in an ASP.NET page
- using Silverlight 4 in an ASP.NET MVC 3 application and accessing data with JSON