jQuery 处理 ajax 调用中的会话超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5238854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 18:47:48  来源:igfitidea点击:

Handling session timeout in ajax calls

asp.net-mvcasp.net-mvc-3jqueryauthorize-attribute

提问by Justin

I'm making an ajax call using jquery to an asp.net mvc controller action:

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

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

When session times out, this call will fail, as the User object is stored in session. I created a custom authorize attribute in order to check if session was lost and redirect to the login page. This works fine for page requests, however it doesn't work for ajax requests, as you can't redirect from an ajax request:

当会话超时时,此调用将失败,因为用户对象存储在会话中。我创建了一个自定义授权属性,以检查会话是否丢失并重定向到登录页面。这适用于页面请求,但不适用于 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;
        }
    }

I read on another thread that when the user isn't authenticated and you make an ajax request, you should set the status code to 401 (unauthorized) and then check for that in js and redirect them to the login page. However, I can't get this working:

我在另一个线程上读到,当用户未通过身份验证并且您发出 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);
            }
        }

Basically, it'll set it to 401, but then it'll continue into the controller action and throw an object ref not set to an instance of an object error, which then returns error 500 back to the client-side js. If I change my custom Authorize attribute to validate ajax requests as well and return false for those that aren't authenticated, that makes the ajax request return my login page, which obviously doesn't work.

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

How do I get this working?

我如何让这个工作?

回答by Darin Dimitrov

You could write a custom [Authorize]attribute which would return JSON instead of throwing a 401 exception in case of unauthorized access which would allow client scripts to handle the scenario gracefully:

您可以编写一个自定义[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);
        }
    }
}

then decorate your controller/actions with it and on the client:

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

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

回答by free4ride

I wouldn't change JsonRequestBehavior to AllowGet. Instead I suggest:

我不会将 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();
            }
        }
    }
}

and add global js ajax errors handler:

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

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

回答by Nicholi

Even though this is well past answered, I think this is the shortest and sweetest answer if you are using .NET 4.5. Little property called SuppressFormsAuthenticationRedirect which was added. Set to true and it will not perform the 302 Redirect to login page.

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

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

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;
        }
    }
}

Assuming you plan on handling the ajax requests fail/error callback, in which you will get a 401 Unauthorized.

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

回答by Shoeb

On Master page add this jquery script ------------

在母版页上添加这个 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>

Add a cs file named with TraceFilter in your project and write a seald class TraceFilterAttribute inheriting to ActionFilterAttribute. Add TraceFilterAttribute class in FilterConfig.cs available in App_Start folder of your project by writing below line.

在你的项目中添加一个以TraceFilter命名的cs文件,写一个继承ActionFilterAttribute的密封类TraceFilterAttribute。在项目的 App_Start 文件夹中可用的 FilterConfig.cs 中添加 TraceFilterAttribute 类,方法如下:

filters.Add(new TraceFilterAttribute());

过滤器。添加(新的跟踪过滤器属性());

Override method OnActionExecuting() in TraceFilterAttribute class. This will automatically check session and if finds session null then calls script available in master page and from their you can go to your choice page.

覆盖 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");
                    }

                }


}
}

}

回答by ShaneA

I was having a similar issue and found this

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

Instead of returning any JSON, just before the response is sent back, force ASP.NET to return a 401 code. In Global.asax:

不是返回任何 JSON,而是在响应被发回之前,强制 ASP.NET 返回 401 代码。在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;
        }
    }

Then you can let the client deal with it in JavaScript/jQuery or whatever you are using

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

回答by IsPha

here is how I handle this in so simple way in my custom authorization , I check if session is out and handle this as un-authorized with a boolean to check if it is really authenticated but not authorized (to redirect to un-authorized page) or it is not authenticated due to session time out ( redirect to Login)

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

 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");
        }


    }

Hope if this guides someone and please if there're comments its appreciated to know them though.

希望这是否能指导某人,如果有评论,希望了解他们。

回答by Dimitri

You might want to try to throw HttpException and catch it in your javascript.

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

throw new HttpException(401, "Auth Failed")

回答by Jathin Prasad

on ajax call if session expired return something like this

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

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

haha...

哈哈...