X-Admin&ABP框架开发-RBAC

  在业务系统需求规划过程当中,一般对于诸如组织机构、用户和角色等这种基础功能,一般是将这部分功能规划到通用子域中,这也说明了,对于这部分功能来说,是系统的基石,整个业务体系是创建于这部分基石之上的,固然,还有诸如多语言、设置管理、认证和受权等。对于这部分功能,ABP中存在这些概念,而且经过Module Zero模块完成了这些概念。html

 

1、角色访问控制之RBAC

  RBAC:Role Based Access Control,基于角色的访问控制,这在目前大多数软件中来说已经算得上是广泛应用了,最多见的结构以下,结构简单,设计思路清晰。前端

  

  可是也存在其它升级版的设计,诸如用户权限表、角色组、用户组的概念等,具体分类有RBAC0、RBAC一、RBAC2等,后者功能愈来愈强大,也愈来愈复杂。java

  • RBAC0:是RBAC的核心思想。
  • RBAC1:是把RBAC的角色分层模型。
  • RBAC2:增长了RBAC的约束模型。
  • RBAC3:整合RBAC2 + RBAC1。

 

2、ABP中的RBAC

  在Abp中,已经集成了这些概念,并在ModuleZero模块中实现了这些概念,基于IdentityServer4的ModuleZero模块完成了封装。对于咱们大多数以业务为中心的开发人员来说,不该该又去造一个轮子,而是应该开好这辆车。首先看下Abp中的RBAC模型git

  

  在这其中权限表中记录了用户与权限,角色与权限两部分。对于权限一般指的是功能权限和数据权限两部分,通常来说,大多指的是功能权限,这种经过角色与权限进行管理便可,如还有用户部分的功能区分,则能够再使用上用户与权限,而对于数据权限,能够利用用户与权限部分,我的用的比较少,可是,能够想象到这么一个场景,针对于一家门店内的多个店长,角色相同即相应的权限相同,但各自关心的数据来源不一样,关心东部、南部等数据,而不关心西部、北部数据,所以能够在数据层面进行划分,好比设置数据来源,东南西北,对于数据来源进行权限关联,这样一来用户自己若是拥有东部数据权限,则只能看到东部数据。bootstrap

 

一、权限声明及应用后端

  在Abp中,须要首先在Core层/Authorization/PermissionNames.cs中声明权限,Abp权限部分设计原则是:先声明再使用mvc

/// <summary>
/// 权限命名 /// </summary>
public static class PermissionNames { #region 顶级权限
    public const string Pages = "Pages"; #endregion

    #region 基础支撑平台
    public const string Pages_Frame = "Pages.Frame"; #region 租户管理
    public const string Pages_Frame_Tenants = "Pages.Frame.Tenants"; #endregion

    #region 组织机构
    public const string Pages_Frame_OrganizationUnits = "Pages.Frame.OrganizationUnits"; public const string Pages_Frame_OrganizationUnits_Create = "Pages.Frame.OrganizationUnits.Create"; public const string Pages_Frame_OrganizationUnits_Update = "Pages.Frame.OrganizationUnits.Update"; public const string Pages_Frame_OrganizationUnits_Delete = "Pages.Frame.OrganizationUnits.Delete"; #endregion

    #region 用户管理
    public const string Pages_Frame_Users = "Pages.Frame.Users"; public const string Pages_Frame_Users_Create = "Pages.Frame.Users.Create"; public const string Pages_Frame_Users_Update = "Pages.Frame.Users.Update"; public const string Pages_Frame_Users_Delete = "Pages.Frame.Users.Delete"; public const string Pages_Frame_Users_ResetPassword = "Pages.Frame.Users.ResetPassword"; #endregion

    #region 角色管理
    public const string Pages_Frame_Roles = "Pages.Roles"; public const string Pages_Frame_Roles_Create = "Pages.Frame.Roles.Create"; public const string Pages_Frame_Roles_Update = "Pages.Frame.Roles.Update"; public const string Pages_Frame_Roles_Delete = "Pages.Frame.Roles.Delete"; #endregion }

  而后在Core层/Authorization/XXXAuthorizationProvider.cs中设置具体权限,在此处设置权限时,能够根据权限设计时候的职责划分,好比若是仅仅是多租户须要这部分,那便设置权限范围为多租户便可。app

public class SurroundAuthorizationProvider : AuthorizationProvider { public override void SetPermissions(IPermissionDefinitionContext context) { #region 顶级权限
        var pages = context.CreatePermission(PermissionNames.Pages, L("Pages")); #endregion

        #region 基础支撑平台
        var frame = pages.CreateChildPermission(PermissionNames.Pages_Frame, L("Frame")); #region 租户管理 frame.CreateChildPermission(PermissionNames.Pages_Frame_Tenants, L("Tenants"), multiTenancySides: MultiTenancySides.Host); #endregion

        #region 组织机构
        var organizationUnits = frame.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits, L("OrganizationUnits")); organizationUnits.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits_Create, L("CreateOrganizationUnit")); organizationUnits.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits_Update, L("EditOrganizationUnit")); organizationUnits.CreateChildPermission(PermissionNames.Pages_Frame_OrganizationUnits_Delete, L("DeleteOrganizationUnit")); #endregion

        #region 用户管理
        var users = frame.CreateChildPermission(PermissionNames.Pages_Frame_Users, L("Users")); users.CreateChildPermission(PermissionNames.Pages_Frame_Users_Create, L("CreateUser")); users.CreateChildPermission(PermissionNames.Pages_Frame_Users_Update, L("UpdateUser")); users.CreateChildPermission(PermissionNames.Pages_Frame_Users_Delete, L("DeleteUser")); users.CreateChildPermission(PermissionNames.Pages_Frame_Users_ResetPassword, L("ResetPassword")); #endregion

        #region 角色管理
        var roles = frame.CreateChildPermission(PermissionNames.Pages_Frame_Roles, L("Roles")); roles.CreateChildPermission(PermissionNames.Pages_Frame_Roles_Create, L("CreateRole")); roles.CreateChildPermission(PermissionNames.Pages_Frame_Roles_Update, L("UpdateRole")); roles.CreateChildPermission(PermissionNames.Pages_Frame_Roles_Delete, L("DeleteRole")); #endregion } }

  在设置完毕后,须要将该类集成到Core层/XXXCoreModule当前模块中,才能使得该部分权限设置生效。async

//配置权限管理
Configuration.Authorization.Providers.Add<SurroundAuthorizationProvider>();

   做为业务的入口,菜单是较为直观的体现方式,如今能够,为菜单分配权限了,拥有权限的人才能看的到菜单,同时后台方法中也要有权限断定,菜单仅做为前端入口上的控制,权限断定做为后端的控制。在MVC层的Startup/XXXNavigationProvider.cs中完成菜单的配置工做,能够配置多级菜单,每一个菜单能够配置相应的权限,在生成菜单断定时,若是父级菜单权限不足,则直接会跳过子级菜单的断定。ide

new MenuItemDefinition(//基础支撑
 PageNames.FrameManage, L(PageNames.FrameManage), icon: "&#xe828;", requiredPermissionName: PermissionNames.Pages_Frame ).AddItem( new MenuItemDefinition(//组织机构
 PageNames.OrganizationUnits, L(PageNames.OrganizationUnits), url: "/OrganizationUnits", icon: "&#xe6cb;", requiredPermissionName: PermissionNames.Pages_Frame_OrganizationUnits ) ).AddItem( new MenuItemDefinition(//用户管理
 PageNames.Users, L(PageNames.Users), url: "/Users", icon: "&#xe6cb;", requiredPermissionName: PermissionNames.Pages_Frame_Users ) ).AddItem( new MenuItemDefinition(//角色管理
 PageNames.Roles, L(PageNames.Roles), url: "/Roles", icon: "&#xe6cb;", requiredPermissionName: PermissionNames.Pages_Frame_Roles ) ).AddItem( new MenuItemDefinition(//系统设置
 PageNames.HostSettings, L(PageNames.HostSettings), url: "/HostSettings", icon: "&#xe6cb;", requiredPermissionName: PermissionNames.Pages_Frame_HostSettings ) )

  在前端页面上,对于按钮级别的控制也经过权限断定,Abp提供了断定方法,利用Razor语法进行按钮控制

@if (await PermissionChecker.IsGrantedAsync(PermissionNames.Pages_Core_DataDictionary_Create)) { <button class="layui-btn layuiadmin-btn-dataDictionary" data-type="addDataDictionary">添加类型</button> }

  在后端方法上,一般我喜欢直接在应用服务中的方法上作权限断定(固然也能够前移到MVC层,可是这样一来,针对于WebApi形式的Host层,又得多加一次断定了),利用AbpAuthorize特性,断定该方法须要哪几个权限才能访问,而在mvc的控制器上作访问认证。

[AbpAuthorize(PermissionNames.Pages_Core_DataDictionary_Create)] private async Task CreateDataDictionaryAsync(CreateOrUpdateDataDictionaryInput input) { }

 

二、角色与权限

   在Abp中,角色信息存储在abprole表中,角色与权限间的关联存储在abppermission这张表中,一个角色有多个权限,若是某个角色的权限被去掉了,这张表中的相关记录将由abp负责删除,咱们只须要完成掌控哪些权限是这个角色有的就行。Abp中已经完成了角色的全部操做,可是前端部分采用的是bootstrap弄的,将其改造一波,成为layui风格。

  

  在建立角色中,主要是将选中的权限挂钩到具体的某个角色上,该部分代码沿用abp中自带的角色权限处理方法。

private async Task CreateRole(CreateOrUpdateRoleInput input) { var role = ObjectMapper.Map<Role>(input.Role); role.SetNormalizedName(); CheckErrors(await _roleManager.CreateAsync(role)); var grantedPermissions = PermissionManager .GetAllPermissions() .Where(p => input.PermissionNames.Contains(p.Name)) .ToList(); await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions); }

  指定角色Id,租户Id及以前声明的权限名称,在abppermission中可查看到具体角色权限。

  

 

三、用户与角色

   一个用户能够承担多个角色,履行不一样角色的义务,做为一个业务系统最基本的单元,abp中提供了这些概念并在Module Zero模块中已经完成了对用户的一系列操做,用户信息存储在AbpUsers表中,用户直接关联的角色保存在AbpUserRoles表中,abp中MVC版本采用的是bootstrap风格,所以,用layui风格完成一次替换,而且,改动一些页面布局。

  

  Abp版本中,因为是土耳其大佬所开发的习惯,针对于姓和名作了拆分,所以对于咱们的使用要作一次处理,我这先简单处理了一下,而且在业务系统中,邮箱时有时无,所以也须要进行考虑。

[AbpAuthorize(PermissionNames.Pages_Frame_Users_Create)] private async Task CreateUser(CreateOrUpdateUserInput input) { var user = ObjectMapper.Map<User>(input.User); user.TenantId = AbpSession.TenantId; user.IsEmailConfirmed = true; user.Name = "Name"; user.Surname = "Surname"; //user.EmailAddress = string.Empty;

    await UserManager.InitializeOptionsAsync(AbpSession.TenantId); foreach (var validator in _passwordValidators) { CheckErrors(await validator.ValidateAsync(UserManager, user, AppConsts.DefaultPassword)); } user.Password = _passwordHasher.HashPassword(user, AppConsts.DefaultPassword); await _userManager.InitializeOptionsAsync(AbpSession.TenantId); CheckErrors(await _userManager.CreateAsync(user, AppConsts.DefaultPassword)); if (input.AssignedRoleNames != null) { CheckErrors(await _userManager.SetRoles(user, input.AssignedRoleNames)); } if (input.OrganizationUnitIds != null) { await _userManager.SetOrganizationUnitsAsync(user, input.OrganizationUnitIds); } CurrentUnitOfWork.SaveChanges(); }

  此处对用户我的单独的权限没有去作处理,依照Abp的文档有那么一句话,大多数应用程序中,基于角色的已经足够使用了,若是想声明特定权限给用户,那么针对于用户自己的角色权限则被覆盖。    

 

 至此,修改整合用户、角色和权限加入到系统中初步完成了,至于一些更为丰富的功能,待逐步加入中,车子再好,司机也得睡觉。

 

 仓库地址:https://gitee.com/530521314/Partner.Surround.git

2019-11-17,望技术有成后能回来看见本身的脚步

原文出处:https://www.cnblogs.com/CKExp/p/11815015.html

相关文章
相关标签/搜索