上一次在 .NET MVC 用戶權限管理示例教程中講解了ASP.NET MVC 經過AuthorizeAttribute類的OnAuthorization方法講解了粗粒度控制權限的方法,接下來說解基於角色的權限控制方法。html
基於角色的權限控制系統RBAC(Role Based Access Control)是目前最流行,也是最通用的權限控制系統。所謂基於角色的權限控制,就是將各個操做權限分組,每個組就是一個角色,舉個例子:管理員擁有全部的權限,編輯就只擁有寫文章和發佈文章的權限,這裏的「管理員」和「編輯」就是一個角色——一系列操做權限的集合。咱們只須要將某個角色賦予某個用戶那麼這個用戶就擁有這個角色下的權限集合。web
如今咱們要作的就是經過把Controller下的每個Action能夠看做是一個權限,而後能夠經過方法將每個權限進行自定義的分組,從而建立一個角色,而後將角色與用戶對應起來。數據庫
這個類能夠拿到ControllerName和ActionName,這樣能夠根據ControllerName和ActionName判斷是什麼樣的操做,以下面的代碼mvc
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Security; 6 using System.Web.Mvc; 7 using System.Web.Routing; 8 9 namespace SampleMVCWebsite 10 { 11 public class RoleAuthorizeAttribute : AuthorizeAttribute 12 { 13 public override void OnAuthorization(AuthorizationContext filterContext) 14 { 15 var isAuth = false; 16 if (!filterContext.RequestContext.HttpContext.Request.IsAuthenticated) 17 { 18 isAuth = false; 19 } 20 else 21 { 22 if (filterContext.RequestContext.HttpContext.User.Identity != null) 23 { 24 var roleApi = new RoleApi(); 25 var actionDescriptor = filterContext.ActionDescriptor; 26 var controllerDescriptor = actionDescriptor.ControllerDescriptor; 27 var controller = controllerDescriptor.ControllerName; 28 var action = actionDescriptor.ActionName; 29 var ticket = (filterContext.RequestContext.HttpContext.User.Identity as FormsIdentity).Ticket; 30 var role = roleApi.GetById(ticket.Version); 31 if (role != null) 32 { 33 isAuth = role.Permissions.Any(x => x.Permission.Controller.ToLower() == controller.ToLower() && x.Permission.Action.ToLower() == action.ToLower()); 34 } 35 } 36 } 37 if (!isAuth) 38 { 39 filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "account", action = "login", returnUrl = filterContext.HttpContext.Request.Url, returnMessage = "您無權查看." })); 40 return; 41 } 42 else 43 { 44 base.OnAuthorization(filterContext); 45 } 46 } 47 } 48 }
其中RoleApi是角色對象管理的API,這裏須要本身設置角色管理。上面的代碼中經過FilterContext的ActionDescriptor對象的ControllerDescriptor就能夠獲取到ControllerName和ActionName。獲取到當前用戶的角色後,經過查看用戶的權限中是否包含了當前訪問的Controller中的方法,就能實現權限驗證。這裏主要使用了ActionDescriptor和ControllerDescriptor,關於這兩個類的介紹能夠參考MSDN官網。ide
PS:這裏用Ticket的Version存儲RoleId。你也能夠用其餘方式。關於Ticket的知識能夠參考微軟MSDN官方文檔,這裏再也不敖述。spa
這個類是繼承Attribute,爲的是給Action方法打上描述標籤,以下面的代碼code
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace SampleMVCWebsite 7 { 8 /// <summary> 9 /// Description Attribute 10 /// </summary> 11 public class DescriptionAttribute:Attribute 12 { 13 public string Name 14 { 15 set; 16 get; 17 } 18 public int No 19 { 20 set; 21 get; 22 } 23 } 24 }
Name和NO和Permission類中是ControllerName、ActionName和ControllerNo、ActionNO是對應的。orm
使用步驟二建立的DescriptionAttribute標籤給Controller標上,以便存儲Permission的時候獲取信息。以下面的代碼:htm
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 namespace SampleMVCWebsite.Controllers 8 { 9 [Description(No = 1, Name = "用戶")] 10 public class UserController : Controller 11 { 12 [RoleAuthorize] 13 [Description(No = 1, Name = "用戶首頁")] 14 public ActionResult Index() 15 { 16 return View(); 17 } 18 [RoleAuthorize] 19 [Description(No = 1, Name = "用戶管理員")] 20 public ActionResult Manage() 21 { 22 return View(); 23 } 24 [RoleAuthorize] 25 [Description(No = 1, Name = "用戶詳情")] 26 public ActionResult Detail() 27 { 28 return View(); 29 } 30 } 31 }
步驟一種有一個role.Permissions,這個Permissions就是當前用戶所擁有的權限集合,在實際的項目開發中是把它存儲在數據庫關係表中的,咱們能夠將Permissions類以下面的代碼這樣定義對象
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace SampleMVCWebsite 7 { 8 public class Permission 9 { 10 /// <summary> 11 /// Permission Id 12 /// </summary> 13 public virtual int Id 14 { 15 set; 16 get; 17 } 18 /// <summary> 19 /// Permission Action No 20 /// </summary> 21 public virtual int ActionNo 22 { 23 set; 24 get; 25 } 26 27 /// <summary> 28 /// Controller No 29 /// </summary> 30 public virtual int ControllerNo 31 { 32 set; 33 get; 34 } 35 36 /// <summary> 37 /// Controller Name 38 /// </summary> 39 public virtual string ControllerName 40 { 41 set; 42 get; 43 } 44 45 /// <summary> 46 /// Permission Action Name 47 /// </summary> 48 public virtual string ActionName 49 { 50 set; 51 get; 52 } 53 54 /// <summary> 55 /// Controller 56 /// </summary> 57 public virtual string Controller 58 { 59 set; 60 get; 61 } 62 63 /// <summary> 64 /// Action 65 /// </summary> 66 public virtual string Action 67 { 68 set; 69 get; 70 } 71 } 72 }
屬性Controller和Action記錄的是權限,ControllerName和ActionName用於顯示UI,ControllerNo和ActionNo用於顯示順序控制。其他根據上面的代碼註釋應該很好理解,這裏就不一一陳述了。
所以你須要將Action輸入數據庫中來實現RoleApi。手動輸入若是方法少還好,可是多了就比較麻煩,這樣咱們可使用.NET的反射機制來進行權限的建立。先看下面的代碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using SampleMVCWebsite.Controllers; 7 8 namespace SampleMVCWebsite 9 { 10 public class InstallController 11 { 12 public class InstallController : Controller 13 { 14 public ActionResult Index() 15 { 16 return View(); 17 } 18 19 [HttpPost] 20 public ActionResult Index() 21 { 22 try 23 { 24 var roleService = new RoleApi(); 25 #region init permission 26 CreatePermission(new UserController()); 27 #endregion 28 29 var allDefinedPermissions = roleService.GetDefinedPermissions(); 30 31 #region 管理員角色初始化 32 var adminPermissions = new List<RolePermissionInfo>(); 33 foreach (var d in allDefinedPermissions) 34 { 35 adminPermissions.Add(new RolePermissionInfo {Permission = d, }); 36 } 37 int adminRoleId = roleService.AddRole(new RoleInfo 38 { 39 Name = "管理員", 40 Description = "", 41 Permissions = adminPermissions, 42 AddDate = DateTime.Now, 43 }); 44 #endregion 45 return RedirectToAction("Admin", "Index"); 46 } 47 catch (Exception ex) 48 { 49 ModelState.AddModelError("", ex.Message); 50 return View(); 51 } 52 } 53 private void CreatePermission(Controller customController) 54 { 55 var roleApi = new RoleApi(); 56 57 var controllerName = ""; 58 var controller = ""; var controllerNo = 0; 59 var actionName = ""; var action = ""; var actionNo = 0; 60 var controllerDesc = new KeyValuePair<string, int>(); 61 62 var controllerType = customController.GetType(); 63 64 //remove controller posfix 65 controller = controllerType.Name.Replace("Controller", ""); 66 controllerDesc = Getdesc(controllerType); 67 if (!string.IsNullOrEmpty(controllerDesc.Key)) 68 { 69 controllerName = controllerDesc.Key; 70 controllerNo = controllerDesc.Value; 71 foreach (var m in controllerType.GetMethods()) 72 { 73 var mDesc = GetPropertyDesc(m); 74 if (string.IsNullOrEmpty(mDesc.Key)) continue; 75 action = m.Name; 76 actionName = mDesc.Key; 77 actionNo = mDesc.Value; 78 roleApi.CreatePermissions(actionNo, controllerNo, actionName, controllerName, controller, action); 79 } 80 } 81 } 82 private KeyValuePair<string, int> Getdesc(Type type) 83 { 84 var descriptionAttribute = (DescriptionAttribute)(type.GetCustomAttributes(false).FirstOrDefault(x => x is DescriptionAttribute)); 85 if (descriptionAttribute == null) return new KeyValuePair<string, int>(); 86 return new KeyValuePair<string, int>(descriptionAttribute.Name, descriptionAttribute.No); 87 } 88 private KeyValuePair<string, int> GetPropertyDesc(System.Reflection.MethodInfo type) 89 { 90 var descriptionAttribute = (DescriptionAttribute)(type.GetCustomAttributes(false).FirstOrDefault(x => x is DescriptionAttribute)); 91 if (descriptionAttribute == null) return new KeyValuePair<string, int>(); 92 return new KeyValuePair<string, int>(descriptionAttribute.Name, descriptionAttribute.No); 93 } 94 } 95 } 96 } 97 上面的代碼首先經過Getdesc來獲取Controller的描述,GetPropertyDesc
方法獲取方法的描述,這裏的方法就是一個獨立的權限,而後經過roleApi的CreatePermissions方法將權限的數據寫入到數據庫中,將數據寫入數據庫的代碼就須要根據本身的數據庫讀寫方法去實現了,強烈建議使用Linq對數據庫進行讀寫。而後再經過roleApi獲取到全部的權限而且將全部的權限綁定到Admin角色,保存對應關係。這樣咱們就完成了權限控制列表生成而且初始化了管理員用戶。
經過上面的示例就能夠完成ASP.NET MVC 基於角色的權限控制系統,上面的代碼中的roleApi須要本身寫代碼實現對數據庫的操做,數據模型就是權限、角色、用戶的數據表以及權限月角色以及角色與用戶的關係表。