大部分系統(tǒng)都會(huì)有權(quán)限模塊,別人家系統(tǒng)的權(quán)限怎么生成的我不知道,我只知道這樣做是可以并且挺好的。
文章中只對(duì)asp.net core的部分代碼進(jìn)行說明 呃 記錄~,mvc版本自行前往倉庫查閱
代碼中的一些特性標(biāo)記后面列出,或前往倉庫查看~
1.根據(jù)特性標(biāo)記生成模塊權(quán)限
先上效果圖,感興趣的前往Demo倉庫地址,不感興趣的關(guān)閉頁面吧~
模型定義
Demo中菜單分為三級(jí),首先使用枚舉定義模塊,FirstModuleMenu為一級(jí)菜單,SecondModuleMenu為二級(jí)菜單,三級(jí)菜單在action方法上由PermissionDescription標(biāo)識(shí)并IsMenu=true的方法
若是頁面功能則為IsMenu=false
可使用的特性標(biāo)記還包含以下幾種,并且權(quán)限驗(yàn)證時(shí)依次遞增:
- 免登錄:AllowAnonymous
- 管理員默認(rèn)權(quán)限: NonePermissionAttribute
- 指定權(quán)限: PermissionDescriptionAttribute
- 依賴權(quán)限(包含有這些的任一權(quán)限都將獲得授權(quán)): ParentPermissionAttribute
//一級(jí)菜單public class FirstModuleMenu{public const string 系統(tǒng)管理 = "系統(tǒng)|icon-setting";public const string 用戶管理 = "用戶|icon-user";}//二級(jí)菜單public enum SecondModuleMenu{[Description(FirstModuleMenu.系統(tǒng)管理)]系統(tǒng)設(shè)置,[Description(FirstModuleMenu.用戶管理)]用戶管理,}//三級(jí)菜單[PermissionDescription(SecondModuleMenu.系統(tǒng)設(shè)置, "站點(diǎn)設(shè)置", true)]public ActionResult SiteSetting(){return Content("站點(diǎn)設(shè)置 System/SiteSetting");}
生成權(quán)限模型集合
定義權(quán)限模型 SysModule.cs
調(diào)用初始化權(quán)限方法
private static List<SysModule> _AllAdminModule { get; set; } = new List<SysModule>();/// <summary>/// 初始化權(quán)限/// </summary>public static void InitPermission(){var result = new List<SysModule>();#region 通過反射讀取Action方法寫入對(duì)應(yīng)權(quán)限至集合//讀取CoreDemo程序集中集成自AdminController的控制器var types = Assembly.Load("CoreDemo").GetTypes().Where(e => e.BaseType.Name == nameof(AdminController));var area = "";//默認(rèn)未使用區(qū)域var now = DateTime.Now;var i = 1;foreach (var type in types){//獲取所有action方法var members = type.GetMethods().Where(e => e.ReturnType.Name == nameof(ActionResult) || e.ReturnType.Name == nameof(IActionResult));foreach (var member in members){//獲取功能列表var attrs = member.GetCustomAttributes(typeof(PermissionDescriptionAttribute), true);if (attrs.Length == 0)continue;//功能對(duì)應(yīng)的二級(jí)菜單var parentMenuEnum = (attrs[0] as PermissionDescriptionAttribute).ParentMenu;var parentMenuName = parentMenuEnum.ToString();//功能對(duì)應(yīng)的一級(jí)菜單 名稱|icon類名var enumArry = parentMenuEnum.GetEnumDescription().Split('|');var mainMenuName = enumArry[0];var existMainMenu = result.Where(e => e.ModuleName == mainMenuName).FirstOrDefault();if (existMainMenu == null){var mainMenu = new SysModule(){Id = i,ParentId = 0,ModuleName = mainMenuName,Icon = enumArry[1] ?? "",Area = area,Controller=string.Empty,Action=string.Empty,IsMenu = true,IsVisible = true,Remark = string.Empty,DisplayOrder = i,CreateTime = now};i++;existMainMenu = mainMenu;existMainMenu.Children = new List<SysModule>();//添加一級(jí)菜單result.Add(existMainMenu);}var existParentMenu = existMainMenu.Children.Where(e => e.ModuleName == parentMenuName).FirstOrDefault();if (existParentMenu == null){var parentMenu = new SysModule(){Id = i,ParentId = existMainMenu.Id,ModuleName = parentMenuName,Icon=string.Empty,Area = area,Controller = string.Empty,Action = string.Empty,IsMenu = enumArry.Length != 3 || bool.Parse(enumArry[2]),IsVisible = true,DisplayOrder = i,CreateTime = now,Children = new List<SysModule>()};i++;existParentMenu = parentMenu;existParentMenu.Children = new List<SysModule>();existMainMenu.Children.Add(existParentMenu);//添加二級(jí)菜單result.Add(existParentMenu);}var menu = new SysModule(){Id = i,ParentId = existParentMenu.Id,Area = area,Action = member.Name,DisplayOrder = i,CreateTime = now,Controller = member.DeclaringType.Name.Substring(0, member.DeclaringType.Name.Length - 10),IsMenu = (attrs[0] as PermissionDescriptionAttribute).IsMenu,Children = new List<SysModule>(),};if (menu.IsMenu)menu.IsVisible = true;menu.ModuleName = (attrs[0] as PermissionDescriptionAttribute).FuncName;i++;existParentMenu.Children.Add(menu);result.Add(menu);}}#endregion//todo 添加到數(shù)據(jù)庫_AllAdminModule = result;}
2.使用過濾器攔截請(qǐng)求進(jìn)行驗(yàn)證
新建特性標(biāo)記 AdminAuthorizeAttribute 繼承Attribute類以及實(shí)現(xiàn)IAuthorizationFilter接口的OnAuthorization方法
不多說,上圖
不多說,上代碼↓_↓
權(quán)限驗(yàn)證過濾器:AdminAuthorizeAttribute
//后臺(tái)權(quán)限驗(yàn)證public class AdminAuthorizeAttribute : Attribute,IAuthorizationFilter{public void OnAuthorization(AuthorizationFilterContext filterContext){//匿名標(biāo)識(shí) 無需驗(yàn)證if (filterContext.Filters.Any(e => (e as AllowAnonymous) != null))return;var adminInfo = GlobalContext.AdminInfo;//此處應(yīng)為獲取的登錄用戶if (adminInfo == null){if(filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest"){filterContext.Result = new JsonResult("未登錄");}else{filterContext.Result =new ContentResult() { Content = "未登錄" };}return;}//對(duì)應(yīng)action方法或者Controller上若存在NonePermissionAttribute標(biāo)識(shí),即表示為管理員的默認(rèn)權(quán)限,只要登錄就有權(quán)限var isNone = filterContext.Filters.Any(e => (e as NonePermissionAttribute) != null);if (isNone)return;//獲取請(qǐng)求的區(qū)域,控制器,action名稱var area = filterContext.RouteData.DataTokens["area"]?.ToString();var controller = filterContext.RouteData.Values["controller"]?.ToString();var action = filterContext.RouteData.Values["action"]?.ToString();var isPermit = false;//校驗(yàn)權(quán)限isPermit = ServiceFactory.CheckAdminPermit(adminInfo.Id, area, controller, action);if (isPermit)return;//此action方法的父輩權(quán)限判斷,只要有此action對(duì)應(yīng)的父輩權(quán)限,皆有權(quán)限訪問var pAttrs = filterContext.Filters.Where(e => (e as ParentPermissionAttribute) != null).ToList();if (pAttrs.Count > 0){foreach (ParentPermissionAttribute pattr in pAttrs){if (!string.IsNullOrEmpty(pattr.Area))area = pattr.Area;isPermit = ServiceFactory.CheckAdminPermit(adminInfo.Id, area, pattr.Controller, pattr.Action);if (isPermit)return;}}if (!isPermit){filterContext.Result = new ContentResult() { Content = "無權(quán)限訪問" };return;}}}
自定義特性標(biāo)記,用于權(quán)限校驗(yàn)
此處的自定義的特性標(biāo)記不能繼承Attribute,因無法在AdminAuthorizeAttribute中的上下文filterContext.Filters中獲取到特性標(biāo)記(不知道咋取特性標(biāo)記,所以用這種方式代替,也更為簡單 冏)
!!!!!!!!!修改: 之前腦袋沒有轉(zhuǎn)過彎來,要使過濾器上下文的Filters中發(fā)現(xiàn)自定義過濾器需要繼承 Attribute, IFilterMetadata
/// <summary>/// 管理員的默認(rèn)權(quán)限/// </summary>public class NonePermissionAttribute : Attribute, IFilterMetadata{}/// <summary>/// 匿名驗(yàn)證/// </summary>public class AllowAnonymous : Attribute, IFilterMetadata{}/// <summary>/// 長輩權(quán)限/// </summary>[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]public class ParentPermissionAttribute : Attribute, IFilterMetadata{/// <summary>/// 區(qū)域/// </summary>public string Area { get; set; }/// <summary>/// 控制器/// </summary>public string Controller { get; set; }/// <summary>/// Action名稱/// </summary>public string Action { get; set; }public ParentPermissionAttribute(string area, string controller, string action){this.Area = area;this.Controller = controller;this.Action = action;}public ParentPermissionAttribute(string controller, string action){this.Controller = controller;this.Action = action;}}
若將代碼全部貼出,有點(diǎn)略顯多余,故,只貼出了部分核心代碼.其他一些模型,擴(kuò)展 請(qǐng)直奔倉庫地址...
或使用git命令克隆MvcPermission分支到MvcPermission文件夾:git clone https://git.coding.net/yimocoding/WeDemo.git -b MvcPermission
補(bǔ)充
- 2017-09-29
突然靈光一現(xiàn),將文中的ResultFilterAttribute特性標(biāo)記替換為Attribute, IFilterMetadata ,果然可以~
故得出:實(shí)現(xiàn)了IFilterMetadata的特性標(biāo)記能夠在過濾器的上下文中獲取到。
轉(zhuǎn)載于:https://www.cnblogs.com/morang/p/7606843.html
總結(jié)
以上是生活随笔為你收集整理的asp.net core权限模块的快速构建的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。