Asp.net AJAX和FormsAuthentication,如何防止FormsAuthentication覆盖HTTP 401?
在一个配置了FormsAuthentication的应用程序中,当用户在没有auth cookie或过期cookie的情况下访问受保护的页面时,ASP.NET发出HTTP 401 Unauthorized,然后FormsAuthentication模块在请求结束前截获此响应,并将其更改为找到的HTTP 302,设置HTTP头“Location:/path/loginurl”将用户代理重定向到登录页面,然后浏览器转到该页面并检索未受保护的登录页面,获得HTTP 200 OK 这确实是一个非常好的主意,当时还没有考虑AJAX 现在,我的应用程序中有一个返回JSON数据的url,它需要对用户进行身份验证。一切正常,问题是如果auth cookie过期,当我的客户端代码调用服务器时,它将得到一个HTTP 200 OK和登录页面的html,而不是HTTP 401 Unauthorized(因为前面已经解释过)。然后我的客户端尝试将登录页面html解析为json,但失败了 接下来的问题是:如何处理来自客户端的过期身份验证?处理这种情况最优雅的解决方案是什么?我需要知道调用何时成功或失败,我想使用HTTP语义来完成 是否可以以安全的跨浏览器方式从客户端读取自定义HTTP头? 如果请求是AJAX请求,是否有方法告诉FormsAuthenticationModule不要执行重定向? 有没有一种方法可以像重写HTTP请求方法一样使用HTTP头重写HTTP状态 我需要表单身份验证,我希望避免重写该模块或编写自己的表单身份验证模块Asp.net AJAX和FormsAuthentication,如何防止FormsAuthentication覆盖HTTP 401?,asp.net,ajax,http,jquery,forms-authentication,Asp.net,Ajax,Http,Jquery,Forms Authentication,在一个配置了FormsAuthentication的应用程序中,当用户在没有auth cookie或过期cookie的情况下访问受保护的页面时,ASP.NET发出HTTP 401 Unauthorized,然后FormsAuthentication模块在请求结束前截获此响应,并将其更改为找到的HTTP 302,设置HTTP头“Location:/path/loginurl”将用户代理重定向到登录页面,然后浏览器转到该页面并检索未受保护的登录页面,获得HTTP 200 OK 这确实是一个非常好的主
问候。我从其他帖子中大量窃取了这个答案,但一个想法可能是在登录页面上实现一个
HttpModule
(该链接中的说明)
您还可以修改该示例HttpModule,使其仅在通过AJAX发出请求时截获重定向,前提是当请求不是通过AJAX发出时,默认行为是正确的:
因此,大致如下:
class AuthRedirectHandler : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.EndRequest+= new EventHandler(context_EndRequest);
}
void context_EndRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
if (app.Response.StatusCode == 302
&& app.Request.Headers["X-Requested-With"] == "XMLHttpRequest"
&& context.Response.RedirectLocation.ToUpper().Contains("LOGIN.ASPX"))
{
app.Response.ClearHeaders();
app.Response.ClearContent();
app.Response.StatusCode = 401;
}
}
#endregion
}
如果你的应用中还有其他合法的302重定向,你也可以确保重定向到你的实际登录页面
然后,您只需将以下内容添加到web.config:
<httpModules>
<add name="AuthRedirectHandler" type="SomeNameSpace.AuthRedirectHandler, SomeNameSpace" />
</httpModules>
总之,我的回答是真实的原始想法,我只是从SO和web的其他部分收集了各种信息。我也有同样的问题,必须在MVC中使用自定义属性。你可以很容易地将其应用到web表单中,如果你的所有页面都继承自s,你可以覆盖基本页面中的页面授权ome基本页(MVC中的全局属性允许相同的事情-覆盖应用程序中所有控制器/操作的OnAuthorization方法) 这是属性的外观:
public class AjaxAuthorizationAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest()
&& !filterContext.HttpContext.User.Identity.IsAuthenticated
&& (filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Count() > 0
|| filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Count() > 0))
{
filterContext.HttpContext.SkipAuthorization = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
filterContext.Result = new HttpUnauthorizedResult("Unauthorized");
filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext);
filterContext.HttpContext.Response.End();
}
}
}
请注意,您需要调用HttpContext.Response.End();否则您的请求将被重定向到login(我因此丢失了一些头发)
在客户端,我使用jQuery ajaxError方法:
var lastAjaxCall = { settings: null, jqXHR: null };
var loginUrl = "yourloginurl";
//...
//...
$(document).ready(function(){
$(document).ajaxError(function (event, jqxhr, settings) {
if (jqxhr.status == 401) {
if (loginUrl) {
$("body").prepend("<div class='loginoverlay'><div class='full'></div><div class='iframe'><iframe id='login' src='" + loginUrl + "'></iframe></div></div>");
$("div.loginoverlay").show();
lastAjaxCall.jqXHR = jqxhr;
lastAjaxCall.settings = settings;
}
}
}
}
这允许您的用户在会话到期时登录,而不会丢失上次显示的表单中键入的任何工作或数据。我在实现时遇到问题。主要是,我的错误日志中充满了
发送HTTP头后服务器无法设置状态的错误
我试图实现这个问题,但还是没有成功
在谷歌上搜索一下,我偶然发现了SuppressFormsAuthenticationRedirect
如果.Net版本>=4.5,则可以将以下代码添加到自定义AuthorizeAttribute
类的HandleUnauthorizedRequest
方法中
public sealed class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
base.HandleUnauthorizedRequest(filterContext);
return;
}
base.HandleUnauthorizedRequest(filterContext);
return;
}
}
重要的部分是if
块。如果您在.Net 4.5上并且已经有了自定义授权,这是最简单的操作。好吧,正如我所说的,我正试图避免重写http模块,基本上是因为我不想维护另一段代码。另外,这个解决方案有点棘手,我想要的是重新编写是否删除对另一个端点的AJAX调用?我无法阻止所有http 302,那么使用http 401:P时我的情况将与现在类似。谢谢。1.您没有“重写http模块”,而是添加了一个非常短、简单的模块(它不处理任何实际的身份验证)处理未经授权的AJAX请求和2.正如我所说的,这个示例并没有包括所有可以想象的过滤器(在本例中甚至不需要)但是登录页面的302重定向要么是a)登录页面,要么是web.config中设置的值。这两个值中的任何一个都应该很容易计算出来并包含在条件中。我认为你不会找到一个包含不到十几行代码的解决方案。无论如何,这个话题已经讨论了一段时间了SP.NET表单:如果您碰巧正在使用MVC,并且注意实际寻找它们,那么也有很多更聪明的服务器端解决方案。我已经看到了所有这些解决方案,其中一些是旧的(2008年那一个),你认为我为什么明确地说我不想写一个模块来处理这个问题?也许在最新的asp.net版本中出现了一些新的东西。在前一段中有三个问题,这些问题就是本文的主题。如果可能的话……如果不快乐的日子也不错。我知道有1000种解决方法和大多数编程问题一样,我回答了你的问题:“如果请求是AJAX请求,有没有办法告诉FormsAuthenticationModule不要执行重定向?“很抱歉,您不满意这样做的方式是一个简单的HTTP模块。除此之外,没有人会在任何老问题出现时犹豫是否要添加新的解决方案。相信我,如果有一颗灵丹妙药的话,它会被钉在一个古老的棺材上
public sealed class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
base.HandleUnauthorizedRequest(filterContext);
return;
}
base.HandleUnauthorizedRequest(filterContext);
return;
}
}