解决方案一、返回响应表示错误
Pro ASP.NET MVC 2 框架为此提供了一种解决方案
如果 Ajax 请求被拒绝授权,那么通常您不希望将 HTTP 重定向返回到登录页面,因为您的客户端代码不希望这样做并且可能会做一些不需要的事情,例如将整个登录页面注入中间用户所在的任何页面。相反,您需要向客户端代码发送回一个更有用的信号(可能是 JSON 格式),以说明请求未被授权。您可以按如下方式实现:
public class EnhancedAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
if (context.HttpContext.Request.IsAjaxRequest()) {
UrlHelper urlHelper = new UrlHelper(context.RequestContext);
context.Result = new JsonResult {
Data = new {
Error = "NotAuthorized",
LogOnUrl = urlHelper.Action("LogOn", "Account")
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
base.HandleUnauthorizedRequest(context);
}
}
然后,您将从控制器中获取一个 JSON 对象,{'Error': 'NotAuthorized', 'LogonUrl': '...'}然后您可以使用该对象来重定向用户。
如果您期望 HTML,则替代响应可能是返回纯字符串,例如NotAuthorized:<your_url>并检查响应是否与此模式匹配。
方案二、返回并处理401 Unautorized状态码
由于必须对每个 ajax 请求的success回调进行检查,这变得非常乏味。能够在全球范围内捕捉到这种情况会很好。最好的解决方案是从服务器返回一个401 Unauthorized状态码并使用 jQuery.ajaxError来捕获它,但这在 IIS/ASP.NET 上是有问题的,因为如果响应具有此状态码,它就会将您重定向到登录页面。只要<authentication>标签存在web.config于此重定向中,如果您不采取任何措施,就会发生[1] 。
所以让我们做点什么吧。这种hacky方式看起来不错。在你的 global.asax.cs
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 302 && Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
{
Context.Response.Clear();
Context.Response.StatusCode = 401;
}
}
401(如果有人知道返回状态码的更好方法,我很想听听。)
通过这样做,您可以防止在请求是 ajax 请求时重定向到登录页面的默认行为。因此,您可以按原样使用默认值AuthorizeAttribute,因为它Application_EndRequest 会处理其余部分。
现在,在 jQuery 代码中,我们将使用该.ajaxError函数来捕获错误。这是一个全局 ajax 事件处理程序,这意味着它将拦截任何 ajax 调用产生的每个错误。它可以附加到任何元素 - 在我的示例中,我刚刚选择body了它,因为它始终存在
$("body").ajaxError(function(event, XMLHttpRequest, ajaxOptions, thrownError) {
if (XMLHttpRequest.status == 401) {
alert("unauthorized");
}
});
这样您就可以将重定向逻辑集中在一个地方,而不必在每个该死的 ajax 请求成功回调上检查它
解决方案3、返回自定义标头
这个答案提供了另一种解决方案。它使用相同类型的全局事件处理程序,但跳过了 hacky 位。如果您未通过身份验证并且请求是 ajax 请求,它会向页面添加自定义标头,并在每个.ajaxComplete. 请注意,链接答案中提供的方法是不安全的,因为它将返回带有可能敏感内容的原始视图,并依赖 javascript 将用户重定向到远离它。通过一些修改,它非常棒,因为它不需要任何黑客攻击,并且逻辑可以集中到一个回调函数中。我能看到的唯一缺点是它没有回复语义正确的状态代码,因为它实际上不是200 OK因为你是401 Unauthorized
我们可以把它烤成另一个习惯EnhancedAuthorizationAttribute
public class EnhancedAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
if (context.HttpContext.Request.IsAjaxRequest())
{
context.HttpContext.Response.AddHeader("REQUIRES_AUTH", "1");
context.Result = new EmptyResult();
}
else
{
base.HandleUnauthorizedRequest(context);
}
}
}
}
现在,每次 ajax 请求完成时,您都会检查此标头:
$('body').ajaxComplete(function(event,request,settings){
if (request.getResponseHeader('REQUIRES_AUTH') === '1'){
alert("unauthorized");
};
});