2011-03-29 62 views
11

我目前有一個項目,我似乎遇到了有關角色的問題,並認爲我會就如何最好地處理該問題提出一些意見。在MVC中處理多個角色 - 基於操作的可訪問性

系統將要求不僅控制特定區域的接入,而且還使用的系統功能(添加用戶,修改用戶信息,查看報表等)可編輯的,靈活的身份

目前,該系統允許用戶有多個角色,每個角色這些角色已經明確定義的訪問/動作的區域,例如:

  • 角色定位可以訪問區1,2,3,並且可以添加用戶。
  • 角色B可以訪問區域1,5,7並且可以修改用戶。
  • 角色C可以訪問區域4,6並且只能查看用戶。

所以用戶可能位於角色A和C,因此可以訪問:1,2,3,4和6,並且可以添加和查看用戶。

我的第一個解決方案是創建一個將所有的訪問/接入方案的可能領域的存儲到一個字典,像這樣一本字典:

Dictionary<string,bool> 

然後當它被實例化它拉的所有屬性從數據庫,然後遍歷角色以確定它們是否可訪問。

所有這些目前工作得很好 - 但是該項目是相當Javascript/jQuery密集,這些選項被客戶端函數調用。我試圖避免與包裹所有這些客戶端功能:

<%if(AccessDictionary[key]) 
    //Enable or Disable Action 
<%}%> 

所以基本上,我想了解一下下面的事情:

  1. 在用戶登錄後,有什麼是存儲此字典的最佳方式?靜態?在會議中?
  2. 什麼是最好的存儲方法,以便在視圖中很容易地訪問字典? (正如我目前看不到繞過我的客戶端功能)

任何意見或想法將不勝感激!

+0

無法通過web.config中的基於位置的授權安全設置來控制此功能嗎? – Holystream 2011-03-29 17:58:44

回答

20

我會將這些信息存儲在認證cookie的用戶數據部分。因此,當用戶登錄:

public ActionResult Login(string username, string password) 
{ 
    // TODO: validate username/password couple and 
    // if they are valid get the roles for the user 

    var roles = "RoleA|RoleC"; 
    var ticket = new FormsAuthenticationTicket(
     1, 
     username, 
     DateTime.Now, 
     DateTime.Now.AddMilliseconds(FormsAuthentication.Timeout.TotalMilliseconds), 
     false, 
     roles 
    ); 
    var encryptedTicket = FormsAuthentication.Encrypt(ticket); 
    var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) 
    { 
     // IIRC this property is only available in .NET 4.0, 
     // so you might need a constant here to match the domain property 
     // in the <forms> tag of the web.config 
     Domain = FormsAuthentication.CookieDomain, 
     HttpOnly = true, 
     Secure = FormsAuthentication.RequireSSL, 
    }; 
    Response.AppendCookie(authCookie); 
    return RedirectToAction("SomeSecureAction"); 
} 

然後我會寫一個會照顧讀取和解析的身份驗證票證和存儲的HttpContext一個通用的用戶自定義屬性authroize。用戶屬性與它對應的角色:

public class MyAuthorizeAttribute : AuthorizeAttribute 
{ 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     if (httpContext.User.Identity.IsAuthenticated) 
     { 
      var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName]; 
      if (authCookie != null) 
      { 
       var ticket = FormsAuthentication.Decrypt(authCookie.Value); 
       var roles = ticket.UserData.Split('|'); 
       var identity = new GenericIdentity(ticket.Name); 
       httpContext.User = new GenericPrincipal(identity, roles); 
      } 
     } 
     return base.AuthorizeCore(httpContext); 
    } 
} 

接下來,您可以裝飾你的控制器/與這個屬性的行動來處理權限:

// Only users that have RoleA or RoleB can access this action 
// Note that this works only with OR => that's how the base 
// authorize attribute is implemented. If you need to handle AND 
// you will need to completely short-circuit the base method call 
// in your custom authroize attribute and simply handle this 
// case manually 
[MyAuthorize(Roles = "RoleA,RoleB")] 
public ActionResult Foo() 
{ 
    ... 
} 

爲了檢查用戶是否只是一個給定的角色:

bool isInRole = User.IsInRole("RoleC"); 

有了這些信息,您就可以開始思考如何組織您的視圖模型。在這些視圖模型中,我將包含布爾型屬性,如CanEditCanViewReport,...將由控制器填充。

現在,如果您在每個操作和視圖中都需要此映射,那麼事情可能會變得重複和無聊。這是全局自定義動作過濾器發揮作用的地方(它們並不存在於ASP.NET MVC 2中,只存在於ASP.NET MVC 3中,因此您可能需要使用此動作過濾器進行裝飾的基本控制器,該控制器模擬的差不多相同功能)。您只需定義這樣的全局動作過濾器,它在每個動作之後執行,並將一些常見視圖模型注入ViewData(聖潔....,不能相信我在發音這些單詞),並因此使其可用於所有視圖的橫向其他行動方式。

最後在視圖中,您將檢查這些布爾值屬性以包含或不包含網站的不同區域。就JavaScript代碼而言,如果它不引人注意,那麼如果這些區域不存在於DOM中,那麼這些代碼將不會運行。如果你需要更細粒度的控制,你總是可以在你的DOM元素上使用HTML5 data-*屬性來給你的外部javascript函數提供關於用戶授權的提示。

+0

感謝像往常一樣非常深入的答案Darin。我會研究整合這樣的東西!另外 - 我會給你另一個+1的「神聖......」ViewData提及。 :) – 2011-03-29 20:59:38

+0

如果您要在cookie中存儲敏感信息(如用戶角色),請確保您擁有所有最新的.NET修補程序。 FormsAuthentication.Encypt()中曾經存在漏洞。 – Ryan 2011-03-31 03:16:16

+0

另外,我非常喜歡數據*的想法。 – Ryan 2011-03-31 03:16:45