大、小項目都要設計權限,都想設計一個通用的權限,把權限作的比較複雜,如今瞭解了ABP的設計思路,以爲設計很簡單,但實現方法與思路回味無窮。javascript
本篇只介紹AbpPermissions的數據庫設計,其它表結構參考源代碼便可[Name(資源文件惟一Id)]、[IsGranted(是否受權)]、[RoleId、UserId(受權於角色或用戶)]html
ABP全部常量數據,都是程序啓動時經過AbpKernelModule一次性加載完成,用的時候直接從內存中讀取便可java
public override void PostInitialize() { RegisterMissingComponents(); IocManager.Resolve<LocalizationManager>().Initialize(); //初始化資源文件 IocManager.Resolve<NavigationManager>().Initialize(); //初始化導航權限 IocManager.Resolve<PermissionManager>().Initialize(); //初始化操做權限 IocManager.Resolve<SettingDefinitionManager>().Initialize(); }
權限分爲前臺權限判斷和後臺權限判斷兩種狀況JS判斷權限是經過引用 <script src="~/AbpScripts/GetScripts" type="text/javascript"></script> 這個腳本,把相關JS對象與方法加載到JS文件數據庫
上圖中有兩個紅框,是後臺構建的兩個導航,MainMenu是系統默認的屬性,Test是自定義屬性,以下代碼async
public class ModuleZeroSampleProjectNavigationProvider : NavigationProvider { public override void SetNavigation(INavigationProviderContext context) { SetNavigation1(context); SetTestNavigation(context); } private void SetNavigation1(INavigationProviderContext context) { context.Manager.MainMenu //默認導航屬性 .AddItem( new MenuItemDefinition( "Questions", new LocalizableString("Questions", ModuleZeroSampleProjectConsts.LocalizationSourceName), url: "#/questions", icon: "fa fa-question", requiredPermissionName: "Questions" //根據變量進行權限判斷 ) ).AddItem( new MenuItemDefinition( "Users", new LocalizableString("Users", ModuleZeroSampleProjectConsts.LocalizationSourceName), url: "#/users", icon: "fa fa-users" ) ); } public const string TestName = "Test"; //自定義導航屬性 private void SetTestNavigation(INavigationProviderContext context) { var testMenu = new MenuDefinition(TestName, new FixedLocalizableString("Frontend menu")); context.Manager.Menus[TestName] = testMenu; testMenu .AddItem( new MenuItemDefinition( "Questions", new LocalizableString("Questions", ModuleZeroSampleProjectConsts.LocalizationSourceName), url: "#/questions", icon: "fa fa-question" ) ).AddItem( new MenuItemDefinition( "Users", new LocalizableString("Users", ModuleZeroSampleProjectConsts.LocalizationSourceName), url: "#/users", icon: "fa fa-users" ) ); } }
JS代碼是經過 NavigationScriptManager 類的 GetScriptAsync()進行加載與權限進行判斷,獲取導航數據經過 abp.nav.menus.MainMenu 數據庫設計
public async Task<IReadOnlyList<UserMenu>> GetMenusAsync(long? userId) //根據當前用戶加載相關導航 { var userMenus = new List<UserMenu>(); foreach (var menu in _navigationManager.Menus.Values) // 默認初始化的全部 導航屬性 { userMenus.Add(await GetMenuAsync(menu.Name, userId)); } return userMenus; } private async Task<int> FillUserMenuItems(long? userId, IList<MenuItemDefinition> menuItemDefinitions, IList<UserMenuItem> userMenuItems) { var addedMenuItemCount = 0; foreach (var menuItemDefinition in menuItemDefinitions) { if (menuItemDefinition.RequiresAuthentication && !userId.HasValue) { continue; } if (!string.IsNullOrEmpty(menuItemDefinition.RequiredPermissionName) && (!userId.HasValue || !(await PermissionChecker.IsGrantedAsync(userId.Value, menuItemDefinition.RequiredPermissionName)))) //根據當前用戶Id和權限判斷當前用戶是否有導航權限 { continue; } var userMenuItem = new UserMenuItem(menuItemDefinition); if (menuItemDefinition.IsLeaf || (await FillUserMenuItems(userId, menuItemDefinition.Items, userMenuItem.Items)) > 0) //遞歸加載層級導航 { userMenuItems.Add(userMenuItem); ++addedMenuItemCount; } } return addedMenuItemCount; }
abp.js 定義了不少方法與屬性,用戶判斷權限的是 abp.auth.hasPermission(),該方法的參數是 後臺Action對應的操做權限,若是該方法返回值爲True,則說明當前用戶被授予了權限。ide
前臺JS經過 AuthorizationScriptManager 類的 GetScript 方法 加載全部權限及當前用戶的權限ui
public async Task<string> GetScriptAsync() { var allPermissionNames = _permissionManager.GetAllPermissions(false).Select(p => p.Name).ToList(); //獲取全部權限 var grantedPermissionNames = new List<string>(); if (AbpSession.UserId.HasValue) { foreach (var permissionName in allPermissionNames) { if (await PermissionChecker.IsGrantedAsync(AbpSession.UserId.Value, permissionName)) { grantedPermissionNames.Add(permissionName); // 獲取當前用戶的權限 } } } var script = new StringBuilder(); script.AppendLine("(function(){"); script.AppendLine(); script.AppendLine(" abp.auth = abp.auth || {};"); script.AppendLine(); AppendPermissionList(script, "allPermissions", allPermissionNames); script.AppendLine(); AppendPermissionList(script, "grantedPermissions", grantedPermissionNames); script.AppendLine(); script.Append("})();"); return script.ToString(); }
權限初始化定義需集成 AuthorizationProvider,以下url
public class ModuleZeroSampleProjectAuthorizationProvider : AuthorizationProvider { public override void SetPermissions(IPermissionDefinitionContext context) { //TODO: Localize (Change FixedLocalizableString to LocalizableString) context.CreatePermission("CanCreateQuestions", new FixedLocalizableString("Can create questions")); context.CreatePermission("CanDeleteQuestions", new FixedLocalizableString("Can delete questions")); context.CreatePermission("CanDeleteAnswers", new FixedLocalizableString("Can delete answers")); context.CreatePermission("CanAnswerToQuestions", new FixedLocalizableString("Can answer to questions"), isGrantedByDefault: true); } }
全部的權限驗證都是經過 AbpUserManager 完成的,如下是幾個重要方法spa
Task<bool> IsGrantedAsync(long userId, string permissionName)
(await UserPermissionStore.HasPermissionAsync(user, new PermissionGrantInfo(permission.Name, false))) 判斷當前用戶是否被授予權限
以QuestionAppService爲例,說明一下權限配置,每一個Service層都要設置權限的 [AbpAuthorize(「Questions」)],當請求時會經過攔截器自動進行權限驗證,每一個Action操做一樣會進行權限攔截 [AbpAuthorize("CanCreateQuestions")] ,權限攔截實現是經過 AuthorizationInterceptor 實現的。
權限驗證是經過以下方法進行操做
internal class AuthorizeAttributeHelper : IAuthorizeAttributeHelper, ITransientDependency { public async Task AuthorizeAsync(IEnumerable<IAbpAuthorizeAttribute> authorizeAttributes) { if (!AbpSession.UserId.HasValue) { throw new AbpAuthorizationException("No user logged in!"); } foreach (var authorizeAttribute in authorizeAttributes) { await PermissionChecker.AuthorizeAsync(authorizeAttribute.RequireAllPermissions, authorizeAttribute.Permissions); //權限檢查 } } }
建議你們先學會如何去用,在用的過程當中會調試再調試,慢慢的就會熟悉源代碼,在不會用的狀況下直接研究源代碼確實不易,俗話說「熟能生巧」應該就是這個意思吧,每一個人的技術水平與能力各不相同,建議只是我的意見。