Handling session timeout in ajax calls

纵饮孤独 提交于 2019-11-26 12:04:04

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:

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

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

[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:

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

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.

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.

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

<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.

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.

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

                }


}
}

}

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:

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

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.

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

throw new HttpException(401, "Auth Failed")

on ajax call if session expired return something like this

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

haha...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!