处理 ajax 调用中的会话超时

Posted

技术标签:

【中文标题】处理 ajax 调用中的会话超时【英文标题】:Handling session timeout in ajax calls 【发布时间】:2011-07-11 11:09:23 【问题描述】:

我正在使用 jquery 对 asp.net mvc 控制器操作进行 ajax 调用:

[AcceptVerbs(HttpVerbs.Post)]
        public ActionResult GetWeek(string startDay)
        
            var daysOfWeek = CompanyUtility.GetWeek(User.Company.Id, startDay);
            return Json(daysOfWeek);
        

当会话超时时,此调用将失败,因为用户对象存储在会话中。我创建了一个自定义授权属性,以检查会话是否丢失并重定向到登录页面。这适用于页面请求,但不适用于 ajax 请求,因为您无法从 ajax 请求重定向:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthorizeUserAttribute : AuthorizeAttribute
    
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        
            if (!httpContext.Request.IsAjaxRequest())
            //validate http request.
                if (!httpContext.Request.IsAuthenticated
                    || httpContext.Session["User"] == null)
                
                    FormsAuthentication.SignOut();
                    httpContext.Response.Redirect("~/?returnurl=" + httpContext.Request.Url.ToString());
                    return false;
                
            
            return true;
        
    

我在另一个线程上读到,当用户未通过身份验证并且您发出 ajax 请求时,您应该将状态代码设置为 401(未授权),然后在 js 中检查并将其重定向到登录页面。但是,我无法正常工作:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
        
            if (Request.IsAjaxRequest() && (!Request.IsAuthenticated || User == null))
            
                filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
            
            else
            
                base.OnActionExecuting(filterContext);
            
        

基本上,它会将其设置为 401,但随后它将继续进入控制器操作并抛出未设置为对象实例的对象引用错误,然后将错误 500 返回给客户端 js .如果我更改我的自定义 Authorize 属性以验证 ajax 请求并为那些未通过身份验证的请求返回 false,这会使 ajax 请求返回我的登录页面,这显然不起作用。

我如何让它工作?

【问题讨论】:

【参考方案1】:

您可以编写一个自定义的 [Authorize] 属性,该属性将返回 JSON 而不是在未经授权的访问情况下引发 401 异常,这将允许客户端脚本优雅地处理该场景:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAuthorizeAttribute : AuthorizeAttribute

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        
            filterContext.Result = new JsonResult
            
                Data = new 
                 
                    // put whatever data you want which will be sent
                    // to the client
                    message = "sorry, but you were logged out" 
                ,
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            ;
        
        else
        
            base.HandleUnauthorizedRequest(filterContext);
        
    

然后用它和在客户端上装饰你的控制器/动作:

$.get('@Url.Action("SomeAction")', function (result) 
    if (result.message) 
        alert(result.message);
     else 
        // do whatever you were doing before with the results
    
);

【讨论】:

噢达林·迪米特洛夫。我爱你的帖子 :-) 每次它对我们都有帮助。 +1 但这意味着所有进行 ajax 调用的客户端代码(无论是使用 jQuery 还是通过 Ajax.ActionLink 或 Ajax.BeginForm)都应该计算在内。这可能适用于一个小项目,但随着网站的发展,维护它最终会变成一场噩梦。 Mosh,大多数客户端框架,例如 jQuery,都有全局错误处理能力。例如,jQuery 有 .ajaxError() ,只要 Ajax 请求完成并出现错误就会触发。这使得在客户端处理错误变得非常容易,即使对于大型项目也是如此。 太棒了。但是这个 jQuery 逻辑我需要在我调用 ajax 的所有地方进行。 jQuery 中有一个全球性的地方可以做到这一点吗? @Murali,当然,你可以注册一个全局的.ajaxComplete()处理程序。【参考方案2】:

我不会将 JsonRequestBehavior 更改为 AllowGet。相反,我建议:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class MyAuthorizeAttribute : AuthorizeAttribute

    public override void OnAuthorization(AuthorizationContext filterContext)
    
        base.OnAuthorization(filterContext);
        OnAuthorizationHelp(filterContext);
    

    internal void OnAuthorizationHelp(AuthorizationContext filterContext)
    

        if (filterContext.Result is HttpUnauthorizedResult)
        
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            
                filterContext.HttpContext.Response.StatusCode = 401;
                filterContext.HttpContext.Response.End();
            
        
    

并添加全局 js ajax 错误处理程序:

   $(document).ajaxError(function (xhr, props) 
        if (props.status === 401) 
            location.reload(); 
        
   

【讨论】:

对我来说,这会更好,因为我使用的 ui 框架无法完全进行所有这些检查。让全球客户端更好地为我捕获所有工作 你能看看我的尝试跟随问题有什么问题吗? ***.com/q/53790824/2798643 有什么方法可以在 Core 3.1 中运行 base.OnAuthorization 方法吗?【参考方案3】:

尽管这个问题已经得到了很好的回答,但如果您使用的是 .NET 4.5,我认为这是最短和最甜蜜的答案。添加了名为 SuppressFormsAuthenticationRedirect 的小属性。设置为 true 不会执行 302 重定向到登录页面。

http://msdn.microsoft.com/en-us/library/system.web.httpresponse.suppressformsauthenticationredirect.aspx

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AjaxAuthorizeAttribute : AuthorizeAttribute

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    
        // returns a 401 already
        base.HandleUnauthorizedRequest(filterContext);
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        
            // we simply have to tell mvc not to redirect to login page
            filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
        
    

假设您计划处理 ajax 请求失败/错误回调,您将在其中得到 401 Unauthorized。

【讨论】:

【参考方案4】:

在母版页上添加这个 jquery 脚本------------

<script type="text/javascript">

   $.ajaxSetup(
        statusCode: 
            403: function () 
                window.location.reload();
            
        
    );


    OR


    $.ajaxSetup(
        error: function (x, e) 
            if (x.status == 403) 
                window.location.reload(); 
            
        
    );

</script>

在你的项目中添加一个名为TraceFilter的cs文件,并写一个继承ActionFilterAttribute的密封类TraceFilterAttribute。 在您的项目的 App_Start 文件夹中可用的 FilterConfig.cs 中添加 TraceFilterAttribute 类,方法是编写以下行。

filters.Add(new TraceFilterAttribute());

覆盖 TraceFilterAttribute 类中的 OnActionExecuting() 方法。这将自动检查会话,如果发现会话为空,则调用母版页中可用的脚本,您可以从它们转到您的选择页面。

[AttributeUsage(AttributeTargets.All)]
public sealed class TraceFilterAttribute : ActionFilterAttribute

public override void OnActionExecuting(ActionExecutingContext filterContext)
    
        if (filterContext != null)
        
HttpSessionStateBase objHttpSessionStateBase = filterContext.HttpContext.Session;
                var userSession = objHttpSessionStateBase["etenetID"];
if (((userSession == null) && (!objHttpSessionStateBase.IsNewSession)) || (objHttpSessionStateBase.IsNewSession))
                
                    objHttpSessionStateBase.RemoveAll();
                    objHttpSessionStateBase.Clear();
                    objHttpSessionStateBase.Abandon();
                    if (filterContext.HttpContext.Request.IsAjaxRequest())
                    
                        filterContext.HttpContext.Response.StatusCode = 403;
                        filterContext.Result = new JsonResult  Data = "LogOut" ;
                    
                    else
                    
                        filterContext.Result = new RedirectResult("~/Admin/GoToLogin");
                    

                






【讨论】:

这里的母版页脚本在会话过期时被触发并重新加载当前页面。当页面重新加载时,然后检查会话并将用户重定向到登录页面进行登录。 多使用jquery脚本先写即。 $.ajaxSetup( statusCode: 403: function () window.location.reload(); );它工作正常。 它为我工作,干得好,谢谢兄弟,我刚刚复制了你的 ajaxSetup 代码,我以不同的方式编写了 c# 代码。我只需要在客户端处理,你帮助了我,非常感谢:)【参考方案5】:

我遇到了类似的问题,发现this

在返回响应之前,强制 ASP.NET 返回 401 代码,而不是返回任何 JSON。在Global.asax:

protected void Application_EndRequest()
    
        var context = new HttpContextWrapper(Context);
        if (context.Request.IsAjaxRequest() && context.Response.StatusCode == 302)
        
            Context.Response.Clear();
            Context.Response.Write("**custom error message**");
            Context.Response.StatusCode = 401;
        
    

然后你可以让客户端用 JavaScript/jQuery 或任何你正在使用的东西来处理它

【讨论】:

【参考方案6】:

这是我在自定义授权中以如此简单的方式处理此问题的方法,我检查会话是否已结束,并使用布尔值将其处理为未经授权,以检查它是否真的经过身份验证但未经授权(重定向到 un-授权页面)或由于会话超时而未通过身份验证(重定向到登录)

 private bool ispha_LoggedIn = false;
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    
        ispha_LoggedIn = false;
        var session = httpContext.Session;
        bool authorize = false;
        if (httpContext.Session["authenticationInfo"] == null)
        

            return authorize;
        

        using (OrchtechHR_MVCEntities db = new OrchtechHR_MVCEntities())
        
            UserAuthenticationController UM = new UserAuthenticationController();
            foreach (var roles in userAssignedRoles)
            
                authorize = UM.IsUserInRole(httpContext.User.Identity.Name, roles);

                if (authorize)
                

                    return authorize;
                

            
        
        ispha_LoggedIn = true;
        return authorize;
    

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    
        if (ispha_LoggedIn==false)
        
            filterContext.Result = new RedirectResult("~/UserAuthentication/LogIn");
        
        else
        
            filterContext.Result = new RedirectResult("~/Dashboard/UnAuthorized");
        


    

希望如果这可以指导某人,如果有 cmet 了解他们,请表示感谢。

【讨论】:

【参考方案7】:

您可能想尝试抛出 HttpException 并在您的 javascript 中捕获它。

throw new HttpException(401, "Auth Failed")

【讨论】:

您永远不会在 javascript 中捕获 401 异常,因为 Forms Authentication 模块会在 javascript 执行之前捕获它并简单地将 302 重定向状态代码返回到登录页面,然后 AJAX 调用将简单地跟随这个重定向最终得到一个 200 状态码,它会执行成功回调。 @Darin Dimitrov - 你知道处理这种情况的正确方法是什么吗?【参考方案8】:

如果会话过期,则在 ajax 调用上返回类似的内容

<script>
$(function()
    location.reload();
);
</script>

哈哈...

【讨论】:

以上是关于处理 ajax 调用中的会话超时的主要内容,如果未能解决你的问题,请参考以下文章

Grails,在 AJAX 调用中处理会话超时

MVC 3 / Jquery AJAX / Session Expires / C# - 在 ajax 调用期间处理会话超时

如果会话超时,如何在 MVC 中的 jquery ajax 调用后重定向到新页面?

ajax 调用无法识别 Spring Security 会话超时

ajax 调用会话超时

如何处理 AJAX 请求中的会话超时