web-dev-qa-db-fra.com

Gestion du délai de session dans les appels ajax

Je passe un appel ajax en utilisant jquery à une action de contrôleur asp.net mvc:

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

Lorsque la session expire, cet appel échouera car l'objet Utilisateur est stocké dans la session. J'ai créé un attribut authorize personnalisé pour vérifier si la session était perdue et rediriger vers la page de connexion. Cela fonctionne bien pour les demandes de page, mais cela ne fonctionne pas pour les demandes ajax, car vous ne pouvez pas rediriger à partir d'une demande 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;
        }
    }

J'ai lu sur un autre sujet que, lorsque l'utilisateur n'est pas authentifié et que vous effectuez une demande ajax, vous devez définir le code d'état sur 401 (non autorisé), puis vérifier cela dans js et les rediriger vers la page de connexion. Cependant, je ne peux pas que cela fonctionne:

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

Fondamentalement, il sera défini sur 401, mais il continuera ensuite dans l'action du contrôleur et jettera une référence d'objet non définie à une instance d'erreur d'objet, qui renverra ensuite l'erreur 500 au js côté client. Si je modifie mon attribut Authorize personnalisé pour valider également les requêtes ajax et renvoie false pour celles qui ne sont pas authentifiées, la demande ajax renvoie ma page de connexion, ce qui ne fonctionne évidemment pas.

Comment puis-je obtenir ce travail?

55
Justin

Vous pouvez écrire un attribut [Authorize] personnalisé qui renverrait JSON au lieu de générer une exception 401 en cas d'accès non autorisé, ce qui permettrait aux scripts client de gérer le scénario avec élégance:

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

puis décorez votre contrôleur/actions avec lui et sur le client:

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

Je ne changerais pas JsonRequestBehavior en AllowGet. Au lieu de cela, je suggère:

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

et ajoutez le gestionnaire global d'erreurs js ajax:

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

Même si cela a déjà été répondu, je pense que c’est la réponse la plus courte et la plus douce si vous utilisez .NET 4.5. Petite propriété appelée SuppressFormsAuthenticationRedirect qui a été ajoutée. Définie sur true, la page 302 Redirect to login (Créer une redirection vers connexion) ne sera pas exécutée.

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

En supposant que vous envisagiez de gérer les demandes ajax, les rappels d’erreur/erreur, dans lesquels vous obtiendrez un 401 non autorisé.

8
Nicholi

Sur la page principale, ajoutez ce 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>

Ajoutez un fichier cs nommé TraceFilter dans votre projet et écrivez une classe Seald TraceFilterAttribute héritant vers ActionFilterAttribute. Ajoutez la classe TraceFilterAttribute dans FilterConfig.cs disponible dans le dossier App_Start de votre projet en écrivant en dessous de la ligne.

filters.Add (new TraceFilterAttribute ());

Remplacez la méthode OnActionExecuting () dans la classe TraceFilterAttribute. Ceci vérifiera automatiquement la session et si trouve la session nulle, le script disponible dans la page maître sera accessible et vous pourrez accéder à la page de votre choix.

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

                }


}
}

}
5
Shoeb

J'avais un problème similaire et j'ai trouvé this

Au lieu de renvoyer un JSON, juste avant que la réponse ne soit renvoyée, forcez ASP.NET à renvoyer un code 401. Dans 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;
        }
    }

Ensuite, vous pouvez laisser le client le gérer avec JavaScript/jQuery ou tout ce que vous utilisez.

4
ShaneA

voici comment je gère ceci de manière si simple dans mon autorisation personnalisée, je vérifie si la session est terminée et le traite comme non autorisé avec un booléen pour vérifier si elle est réellement authentifiée mais non autorisée (pour rediriger vers une page non autorisée) ou il n'est pas authentifié en raison du délai d'expiration de la session (redirection vers la connexion) 

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


    }

J'espère que cela guidera quelqu'un et s'il vous plaît, s'il y a des commentaires, il est apprécié de les connaître.

1
IsPha

Vous voudrez peut-être essayer de lancer l'exception HttpException et de l'attraper dans votre javascript.

throw new HttpException(401, "Auth Failed")
0
Dimitri