2012-01-23 60 views
3

我在現有系統上構建ASP.NET UI,該系統由每個項目的單獨SQL Server數據庫組成。 「企業」數據庫列出了允許匿名用戶選擇要工作的項目的所有當前項目。項目名稱存儲在會話變量中。當需要登錄時,從項目名稱指示的數據庫中獲得用戶名/密碼/角色等。我已經實現了我自己的基本成員資格和角色提供者來做到這一點,通過web.config中的更改來指定特定頁面所需的角色。 (我不使用標準的ASP.NET配置工具來管理用戶,我有現有的應用程序與我的用戶表一起工作)。跨多個項目的ASP.NET用戶身份驗證

這一切似乎最初都起作用,但我發現當授權系統檢查當前用戶所屬的角色以確定頁面是否可訪問時,會話變量尚未加載。因此,如果我們在web.config中允許角色=「xxx」> <,那麼授權系統會在會話數據加載之前觸發,因此在我知道應該使用哪個項目數據庫之前觸發。

[具體做法是:HttpContext.Current.Session爲空時調用RoleProvider.GetRolesForUser由]

如果誰已經解決了這個問題應該確切地知道我在說什麼。因此,我的問題是:

A)什麼是該方案的「最佳實踐」解決方案?

B)我可以將項目名稱存儲在授權階段可用的其他地方(不是會話變量)嗎?

[更新:是的 - 我們可以使用cookie,假設我們不要求Cookie的操作]

C)有沒有辦法來手動獲得在這個時間較早會話變量?

我嘗試了一個選項來緩存cookie中的角色,但是經過幾分鐘的測試後,我發現GetRolesForUsers仍然被調用。

感謝

更新:
這是問題的根源,其暗示的另一種描述「應用程序可以緩存在緩存或應用程序對象的信息。」:
http://connect.microsoft.com/VisualStudio/feedback/details/104452/session-is-null-in-call-to-getrolesforuser

更新:
這看起來像這裏發現的同樣的問題:
Extending the RoleProvider GetRolesForUser()

更新:
有一個關於在FormsAuthenticationTicket中使用UserData的建議,但即使沒有登錄,我也需要這些數據。

+0

你在哪裏設置的驗證?如果有必要,我傾向於在會員供應商中設置會話,並且通常會起作用。 –

+0

當RoleProvider.GetRolesForUser()被調用時,我發現HttpContext.Current.Sessions爲null,這意味着我不知道要查找哪個數據庫以獲取用戶詳細信息。 – Etherman

+0

我已經解決了使用cookie的主要問題。而不是將項目名稱保存到Session [],我將它保存到Response.Cookies [],然後在GetRolesForUser()中使用HttpContext.Current.Request.Cookies [「ProjectName」] .value值。對於我目前的目的來說,這似乎有效。不過,我看到這個中斷的無Cookie功能。 – Etherman

回答

1

我還沒有發現任何「最佳實踐」在這種情況下寫上去,但這裏是我已經解決:

1)爲了支持匿名用戶(即SQL數據庫中的項目之間切換)我只需使用會話變量來跟蹤項目選擇。我有一個全局屬性,它使用此項目選擇來在需要時提供相應的SQL連接字符串。

2)爲了支持在應用了角色限制的頁面上對GetRolesForUser()的調用,我們不能使用會話變量,因爲如前所述,當實際調用GetRolesForUser()時,會話變量尚未初始化(並且我沒有辦法在請求週期的這個早期階段迫使它進入)。

3)唯一的選擇是使用cookie,或者使用Forms Authentication工單的UserData字段。我瀏覽了很多有關使用會話/ cookie/ID鏈接到應用程序緩存中存儲的對象的理論(當會話不可用時可用),但最終正確的選擇是將此數據放在身份驗證票證中。

4)如果用戶登錄到一個項目,它通過一個ProjectName/UserName對,因此我們在任何地方跟蹤用戶的身份驗證,我們都需要這些數據。在微不足道的測試中,我們可以使用單獨的cookie中的用戶名和單獨的cookie中的項目名稱,但它們可能會不同步。例如,如果我們在項目名稱中使用會話cookie,並在登錄時勾選「記住我」(爲身份驗證票證創建永久性cookie),那麼當會話cookie過期時(瀏覽器關閉時,我們可以以用戶名結尾但沒有項目名稱)。因此,我手動將項目名稱添加到身份驗證票證的UserData字段。

5)我還沒有弄清楚如何在不明確設置cookie的情況下操作UserData字段,這意味着我的解決方案無法在「無Cookie」會話模式下工作。

最終的代碼證明是相對簡單的。

我重寫一個LoginView在登錄頁面的身份驗證事件:

// 
    // Add project name as UserData to the authentication ticket. 
    // This is especially important regarding the "Remembe Me" cookie - when the authentication 
    // is remembered we need to know the project and user name, otherwise we end up trying to 
    // use the default project instead of the one the user actually logged on to. 
    // 
    // http://msdn.microsoft.com/en-us/library/kybcs83h.aspx 
    // http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.remembermeset(v=vs.100).aspx 
    // http://www.hanselman.com/blog/AccessingTheASPNETFormsAuthenticationTimeoutValue.aspx 
    // http://www.csharpaspnetarticles.com/2009/02/formsauthentication-ticket-roles-aspnet.html 
    // http://www.hanselman.com/blog/HowToGetCookielessFormsAuthenticationToWorkWithSelfissuedFormsAuthenticationTicketsAndCustomUserData.aspx 
    // http://stackoverflow.com/questions/262636/cant-set-formsauthenicationticket-userdata-in-cookieless-mode 
    // 
    protected void LoginUser_Authenticate(object sender, AuthenticateEventArgs e) 
    { 
     string userName = LoginUser.UserName; 
     string password = LoginUser.Password; 
     bool rememberMe = LoginUser.RememberMeSet; 
     if ([ValidateUser(userName, password)]) 
     { 
      // Create the Forms Authentication Ticket 
      FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
       1, 
       userName, 
       DateTime.Now, 
       DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes), 
       rememberMe, 
       [ ProjectName ], 
       FormsAuthentication.FormsCookiePath); 

      // Create the encrypted cookie 
      HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket)); 
      if (rememberMe) 
       cookie.Expires = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes); 

      // Add the cookie to user browser 
      Response.Cookies.Set(cookie); 

      // Redirect back to original URL 
      // Note: the parameters to GetRedirectUrl are ignored/irrelevant 
      Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, rememberMe)); 
     } 
    } 

我有這樣的全球性方法來返回項目名稱:

/// <summary> 
    /// SQL Server database name of the currently selected project. 
    /// This name is merged into the connection string in EventConnectionString. 
    /// </summary> 
    public static string ProjectName 
    { 
     get 
     { 
      String _ProjectName = null; 
      // See if we have it already 
      if (HttpContext.Current.Items["ProjectName"] != null) 
      { 
       _ProjectName = (String)HttpContext.Current.Items["ProjectName"]; 
      } 
      // Only have to do this once in each request 
      if (String.IsNullOrEmpty(_ProjectName)) 
      { 
       // Do we have it in the authentication ticket? 
       if (HttpContext.Current.User != null) 
       { 
        if (HttpContext.Current.User.Identity.IsAuthenticated) 
        { 
         if (HttpContext.Current.User.Identity is FormsIdentity) 
         { 
          FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity; 
          FormsAuthenticationTicket ticket = identity.Ticket; 
          _ProjectName = ticket.UserData; 
         } 
        } 
       } 
       // Do we have it in the session (user not logged in yet) 
       if (String.IsNullOrEmpty(_ProjectName)) 
       { 
        if (HttpContext.Current.Session != null) 
        { 
         _ProjectName = (string)HttpContext.Current.Session["ProjectName"]; 
        } 
       } 
       // Default to the test project 
       if (String.IsNullOrEmpty(_ProjectName)) 
       { 
        _ProjectName = "Test_Project"; 
       } 
       // Place it in current items so we do not have to figure it out again 
       HttpContext.Current.Items["ProjectName"] = _ProjectName; 
      } 
      return _ProjectName; 
     } 
     set 
     { 
      HttpContext.Current.Items["ProjectName"] = value; 
      if (HttpContext.Current.Session != null) 
      { 
       HttpContext.Current.Session["ProjectName"] = value; 
      } 
     } 
    } 
1

您不能將項目選擇回發到某個頁面,將該選擇添加到會話中,然後重定向到適當的受保護頁面,其中auth將啓動並強制登錄?

ASP.NET會話不會以cookie的形式創建,除非您至少放置了一個項目。

+0

使用會話[]不起作用,因爲如果我們爲某個頁面的web.config文件添加角色限制,則在確定Session []數據之前會觸發角色提供程序以確定當前用戶的角色(HttpContext在調用GetRolesForUser()時,Current.Sessions爲null)。但是,我可以使用Cookie來存儲數據並在GetRolesForUser()期間檢索它。 – Etherman

+0

我以爲你說的下拉列表是在一頁上 - 這意味着你不知道用戶是誰,更不用說他們的角色了。但是,我很高興你明白了...... – 2012-02-01 07:33:32

+0

這是正確的 - 項目選擇與用戶登錄無關,所以匿名用戶可能已經選擇了項目(我粘貼在cookie中)。當我們轉到需要特定角色的頁面(已登錄的授權用戶)時,我發現用戶是否屬於此頁面生命週期中所需的角色(調用GetRolesForUser)ocurrs之前BEFORE ASP.NET已經填充了Session []數據(Session爲空)。因此,我可以使用cookie而不是會話變量。 – Etherman