2010-04-07 116 views
14

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

但是,我的问题是,在一些页面上有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建议。

+0

@Andrew - 这是一个优雅的解决方案。无论如何,filterContext.HttpContext.Response.Redirect(“/ error/xxx”); (或其他)工作? – Keltex 2010-04-07 22:05:39

+0

@Keltex:我可以按照你的建议将它移动到一个视图中 - 但在我的许多AJAX调用中,它们返回的是没有HTML的原始数据(如值列表),而在其他情况下,它们可能会返回整个表格式良好的数据。因此,对于原始数据的“最低公分母”可能效果最好。我会玩。 – 2010-04-07 23:00:17

回答

16

具体来说,我不知道有关于它的任何最佳实践,但我现在正在为我们的应用程序执行此操作。我们选择了一种客户端解决方案,在该解决方案中,我们将会话超时值输出到母版页中的某个JavaScript中,并计算会话何时到期。

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

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

+2

+1:与每次请求检查会话相比,这是一个影响较小,代码较少的解决方案。 – 2010-04-07 20:55:12

+2

非常好。我完全打算这样做。 – Will 2010-04-07 21:08:28

+0

必须查看“gank”意味着什么...... :-)我想我会实施这个以及根据Keltex的建议提供一些AJAX安全措施。谢谢! – 2010-04-07 21:42:19

2

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

1

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

+0

谢谢!我在OnActionExecuting(见上面)中实现了这一点。 – 2010-04-07 21:54:55

+0

授权属性是确定的,但您必须正确修改它。检查'User.Identity.IsAuthenticated'是不一样的。 – LukLed 2010-04-07 22:57:04

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> 

我把全局AJAX错误处理程序,并在埃弗特$.post失败,403错误,响应消息提醒和用户重定向到注销页。现在我不必在每个$.post请求中处理错误,因为它是全局处理的。

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

您对此有何看法?

编辑:

关于从[授权]属性辞职:[授权]不仅是检查Identity.IsAuthenticated。它还处理页面缓存(因此你不会缓存需要认证的材料)和重定向。没有必要复制此代码。

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 
    // ... 
}); 
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字符串。为简洁起见,我省略了实施。