ASP.NET MVC会话到期

时间:2010-04-07 20:47:17

标签: asp.net-mvc ajax session login

我们有一个需要登录的内部ASP.NET MVC应用程序。登录工作很好,并做了预期的事情。我们的会话有效期为15分钟。在该段时间内坐在单个页面上之后,用户已经丢失了会话。如果他们尝试刷新当前页面或浏览到另一个页面,他们将获得登录页面。我们保存他们的请求,因此一旦他们登录,他们就可以继续访问他们请求的页面。这很有效。

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

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

更新:

所以我继续在我OnActionExecuting(按照Keltex的建议)中实现了这一点

  if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
  {
    if (filterContext.HttpContext.Request.IsAjaxRequest())
    {
      filterContext.HttpContext.Response.Write("Invalid session -- please login!");
      filterContext.HttpContext.Response.End();
    }
    else
    {
      ...
    }
  }

这肯定会让事情变得更好 - 现在即使他们有两个标签(一个带有一些可以触发的AJAX调用)并且他们在第二个标签中明确注销,他们会立即获得更有意义的东西而不是一堆搞砸的AJAX数据。

我仍然认为我会实施Javascript倒计时以及womp建议。

6 个答案:

答案 0 :(得分:16)

具体来说,我不知道有任何关于它的最佳做法,但我现在正在为我们的应用做这件事。我们选择了一个客户端解决方案,我们将会话超时值输出到母版页中的某些javascript中,并计算会话何时到期。

前一分钟,我们弹出一个模态对话框,上面写着“你还在吗?”倒数计时器。一旦计时器到达0:00,我们就会将浏览器重定向到登录页面。

使用最少量的javascript来实现时间和计时器计算,以及一个简单的.ashx处理程序,如果用户点击“我回来了”,它将刷新会话!在会话到期之前的对话框上。这样,如果他们及时返回,他们可以在没有任何导航的情况下刷新会话。

答案 1 :(得分:7)

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

修改授权属性:

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时返回Http403ResultHttp403Result将StatusCode更改为403并将消息返回给用户作为响应。属性(authorize参数)中还有一些额外的逻辑,因为我在基本控制器中打开[Authorize]并在某些页面中禁用它。

其他重要的部分是客户端对此响应的全局处理。这就是我在Site.Master中放置的内容:

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

我放置了GLOBAL ajax错误处理程序,当evert $.post因403错误而失败时,响应消息会被警告,用户将被重定向到注销页面。现在我不必处理每个$.post请求中的错误,因为它是全局处理的。

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

你怎么看?

编辑:

关于从[授权]属性中退出:[授权]不仅仅是关于检查Identity.IsAuthenticated。它还处理页面缓存(因此您不需要缓存需要身份验证的材料)和重定向。无需复制此代码。

答案 2 :(得分:2)

您可以查看可以在Ajax.BeginForm()中设置的AjaxOptions。有一个OnBegin设置,您可以将其与javascript函数关联,该函数可以调用Controller方法以确认会话仍然有效,如果没有,则使用window.location重定向到登录页面。

答案 3 :(得分:1)

部分问题似乎是你让框架做了所有事情。我不会用[Authorize]属性装饰你的AJAX方法。而是检查User.Identity.IsAuthenticated,如果它返回false,则创建合理的错误消息。

答案 4 :(得分:0)

我的解决方案在登录表单上使用了一个元标记,并使用了一些Javascript / jQuery。

<强> LogOn.cshtml

<html>
  <head>
    <meta data-name="__loginform__" content="true" />
    ...
  </head>
  ...
</html>

<强> Common.js

var Common = {
    IsLoginForm: function (data) {
        var res = false;

        if (data.indexOf("__loginform__") > 0) {
            // Do a meta-test for login form
            var temp =
                $("<div>")
                    .html(data)
                    .find("meta[data-name='__loginform__']")
                    .attr("content");

            res = !!temp;
        }
        return res;
    }
};

AJAX代码

$.get(myUrl, myData, function (serverData) {
    if (Common.IsLoginForm(serverData)) {
        location.reload();
        return;
    }

    // Proceed with filling your placeholder or whatever you do with serverData response
    // ...
});

答案 5 :(得分:0)

以下是我的表现......

在我的基本控制器中

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

然后在我的全球.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字符串。为简洁起见,我省略了实现。