2010-04-07 79 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字符串。爲簡潔起見,我省略了實施。