ABP 初探 之 權限設計


      大、小項目都要設計權限,都想設計一個通用的權限,把權限做的比較復雜,現在了解了ABP的設計思路,覺得設計很簡單,但實現方法與思路耐人尋味。

  本篇只介紹AbpPermissions的數據庫設計,其它表結構參考源代碼即可[Name(資源文件唯一Id)]、[IsGranted(是否授權)]、[RoleId、UserId(授權於角色或用戶)]

  ABP所有常量數據,都是程序啟動時通過AbpKernelModule一次性加載完成,用的時候直接從內存中讀取即可

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是自定義屬性,如下代碼

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"
                        )
                );
        }
    }
View Code

  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;
        }
View Code

  abp.js 定義了很多方法與屬性,用戶判斷權限的是 abp.auth.hasPermission(),該方法的參數是 后台Action對應的操作權限,如果該方法返回值為True,則說明當前用戶被授予了權限。

  前台JS通過 AuthorizationScriptManager 類的 GetScript 方法 加載所有權限及當前用戶的權限

 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();
        }
View Code

  權限初始化定義需集成 AuthorizationProvider,如下

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);
        }
    }
View Code

  所有的權限驗證都是通過 AbpUserManager 完成的,以下是幾個重要方法

  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);      //權限檢查
            }
        }
    }
View Code

  

  建議大家先學會如何去用,在用的過程中會調試再調試,慢慢的就會熟悉源代碼,在不會用的情況下直接研究源代碼確實不易,俗話說“熟能生巧”應該就是這個意思吧,每個人的技術水平與能力各不相同,建議只是個人意見。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM