21

我有一个使用 jQuery 从控制器操作加载部分视图的视图。控制器操作由 Authorize 属性修饰,如果用户在调用该操作时超时,而不是被重定向到正确的 LogOn 页面,则 LogOn 页面将插入到局部视图所在的视图中。

这里有另一篇文章描述了一个使用 jQuery 的解决方案,但不幸的是它不包含我需要的任何代码示例,因为我对 jQuery 不是很熟悉(并且是 Web 开发的新手),我需要解决这个问题我会尽快。

我想做的就是将用户重定向到正确的登录页面,这对我来说听起来很直接,但到目前为止似乎还没有。如果 jQuery 是要走的路,有人可以发布一个代码示例,那真的会帮助我。

谢谢。

4

2 回答 2

34

解决方案一、返回响应表示错误


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");
    };
});
于 2010-11-17T02:11:43.867 回答
3

对于 Simen Echholt 第二种解决方案,您可以使用

$.ajaxSetup({ global: true,
    statusCode: {
        401: function () {
            alert("unauthorized");
        }
    }
});

在状态码 401 上绑定一些你的函数

于 2012-04-17T02:36:55.733 回答