Asp.net mvc ASP.NET MVC会话过期

Asp.net mvc ASP.NET MVC会话过期,asp.net-mvc,ajax,session,login,Asp.net Mvc,Ajax,Session,Login,我们有一个需要登录的内部ASP.NET MVC应用程序。登录效果很好,并且做了预期的事情。我们的会议将在15分钟内结束。在一个页面上停留了一段时间后,用户失去了会话。如果他们试图刷新当前页面或浏览到另一个页面,他们将获得一个登录页面。我们保存他们的请求,以便他们登录后可以继续访问他们请求的页面。这很有效 然而,我的问题是,在某些页面上存在AJAX调用。例如,他们可能会填写表单的一部分,然后离开,让他们的会话过期。当他们回来时,屏幕仍然显示。如果他们只是填写一个框(这将进行AJAX调用),AJAX

我们有一个需要登录的内部ASP.NET MVC应用程序。登录效果很好,并且做了预期的事情。我们的会议将在15分钟内结束。在一个页面上停留了一段时间后,用户失去了会话。如果他们试图刷新当前页面或浏览到另一个页面,他们将获得一个登录页面。我们保存他们的请求,以便他们登录后可以继续访问他们请求的页面。这很有效

然而,我的问题是,在某些页面上存在AJAX调用。例如,他们可能会填写表单的一部分,然后离开,让他们的会话过期。当他们回来时,屏幕仍然显示。如果他们只是填写一个框(这将进行AJAX调用),AJAX调用将返回登录页面(在AJAX应该返回实际结果的任何div中)。这看起来很可怕

我认为解决方案是使页面本身过期(这样当会话终止时,它们会自动返回到登录屏幕,而无需任何操作)。然而,我想知道是否有关于如何最好地实现这一点的意见/想法,特别是关于ASP.NET MVC中的最佳实践

更新:

因此,我继续在我的
OnActionExecuting
中实现了这一点(根据Keltex的建议)

这肯定会让事情变得更好——现在,即使他们有两个选项卡(一个带有一些可以触发的AJAX调用),并且在第二个选项卡中显式注销,他们也会立即得到更有意义的东西,而不是一堆乱七八糟的AJAX数据


我仍然认为我将实现womp建议的Javascript倒计时。

您可以查看可以在Ajax.BeginForm()中设置的AjaxOption。有一个OnBegin设置,您可以将其与javascript函数相关联,该函数可以调用控制器方法来确认会话仍然有效,如果无效,则使用
窗口重定向到登录页面。位置

具体地说,我不知道有任何与此相关的最佳实践,但我现在正在为我们的应用程序这么做。我们选择了客户端解决方案,将会话超时值输出到母版页中的一些javascript中,并计算会话何时过期

在开始前5分钟,我们弹出一个模式对话框,说“你还在吗?”并带有倒计时。一旦计时器达到0:00,我们将浏览器重定向到登录页面


它使用最少的javascript来执行时间和计时器计算,以及一个简单的.ashx处理程序来实现,如果用户在会话到期前单击对话框上的“我回来了”,该处理程序将刷新会话。这样,如果他们及时返回,他们就可以刷新会话,而无需任何导航。

部分问题似乎是您让框架完成了所有工作。我不会用
[Authorize]
属性装饰您的AJAX方法。相反,检查
User.Identity.IsAuthenticated
,如果返回false,则创建合理的错误消息。

我昨天问了类似的问题。以下是我的解决方案:

修改的授权属性:

public class OptionalAuthorizeAttribute : AuthorizeAttribute
{
    private class Http403Result : ActionResult
    {
        public override void ExecuteResult(ControllerContext context)
        {
            // Set the response code to 403.
            context.HttpContext.Response.StatusCode = 403;
            context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue);
        }
    }

    private readonly bool _authorize;

    public OptionalAuthorizeAttribute()
    {
        _authorize = true;
    }

    //OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller. 
    //That is why parameter is introduced.
    public OptionalAuthorizeAttribute(bool authorize)
    {
        _authorize = authorize;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        //When authorize parameter is set to false, not authorization should be performed.
        if (!_authorize)
            return true;

        var result = base.AuthorizeCore(httpContext);

        return result;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            //Ajax request doesn't return to login page, it just returns 403 error.
            filterContext.Result = new Http403Result();
        }
        else
            base.HandleUnauthorizedRequest(filterContext);
    }
}
HandleUnauthorizedRequest
被覆盖,因此在使用Ajax时返回
Http403Result
Http403Result
将StatusCode更改为403,并将消息返回给用户作为响应。属性(
authorize
参数)中还有一些附加逻辑,因为我在基本控制器中打开了
[authorize]
,并在某些页面中禁用它

另一个重要部分是客户端对该响应的全局处理。这是我在站点中放置的内容。主:

<script type="text/javascript">
    $(document).ready(
        function() {
            $("body").ajaxError(
                function(e,request) {
                    if (request.status == 403) {
                        alert(request.responseText);
                        window.location = '/Logout';
                    }
                }
            );
        }
    );
</script>

$(文件)。准备好了吗(
函数(){
$(“body”).ajaxError(
功能(e,请求){
if(request.status==403){
警报(request.responseText);
window.location='/Logout';
}
}
);
}
);
我放置了全局ajax错误处理程序,当evert
$.post
失败并出现403错误时,响应消息将被警告,用户将被重定向到注销页面。现在我不必处理每个
$.post
请求中的错误,因为它是全局处理的

为什么是403,而不是401?401由MVC框架内部处理(这就是为什么在授权失败后重定向到登录页面)

你觉得怎么样

编辑:


关于从[Authorize]辞职[Authorize]属性:[Authorize]不仅仅是关于检查标识。I已验证。它还处理页面缓存(这样您就不会缓存需要身份验证的内容)和重定向。无需复制此代码。

我的解决方案在登录表单上使用一个meta标记和一点Javascript/jQuery

LogOn.cshtml

<html>
  <head>
    <meta data-name="__loginform__" content="true" />
    ...
  </head>
  ...
</html>
我是这样做的

在我的基本控制器中

 protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.StatusCode = 403;
                filterContext.HttpContext.Response.Write(SessionTimeout);
                filterContext.HttpContext.Response.End();
            }
        }
    }
然后在我的global.js文件中

$.ajaxSetup({
error: function (x, status, error) {
    if (x.status == 403) {
        alert("Sorry, your session has expired. Please login again to continue");
        window.location.href = "/Account/Login";
    }
    else {
        alert("An error occurred: " + status + "nError: " + error);
    }
}
}))


SessionTimeout变量是一个noty字符串。为了简洁起见,我省略了实现。

+1:这将是一个影响更小、代码更少的解决方案,比检查每个请求的会话要少。这非常好。我完全要把这件事搞糟了。我得查一下“搞糟”是什么意思…:-)我想我将实现这一点,并根据Keltex的建议提供一些AJAX保护。非常感谢。谢谢我在OnActionExecuting中实现了这一点(见上文)。Authorize属性是可以的,但是您必须正确地修改它。检查
User.Identity.IsAuthenticated
是不同的。@Andrew-这是一个优雅的解决方案。另外,将filterContext.HttpContext.Response.Redirect(“/error/xxx”);(或其他什么)有效吗?@Keltex:我可以按照你的建议将其移动到视图中——但在我的许多AJAX调用中,它们返回的是原始数据(如值列表),没有HTML,而在其他调用中,它们返回的可能是一整张表
$.get(myUrl, myData, function (serverData) {
    if (Common.IsLoginForm(serverData)) {
        location.reload();
        return;
    }

    // Proceed with filling your placeholder or whatever you do with serverData response
    // ...
});
 protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.StatusCode = 403;
                filterContext.HttpContext.Response.Write(SessionTimeout);
                filterContext.HttpContext.Response.End();
            }
        }
    }
$.ajaxSetup({
error: function (x, status, error) {
    if (x.status == 403) {
        alert("Sorry, your session has expired. Please login again to continue");
        window.location.href = "/Account/Login";
    }
    else {
        alert("An error occurred: " + status + "nError: " + error);
    }
}