X-Admin&ABP框架開發-RBAC

  在業務系統需求規劃過程當中,一般對於諸如組織機構、用戶和角色等這種基礎功能,一般是將這部分功能規劃到通用子域中,這也說明了,對於這部分功能來說,是系統的基石,整個業務體系是創建於這部分基石之上的,固然,還有諸如多語言、設置管理、認證和受權等。對於這部分功能,ABP中存在這些概念,而且經過Module Zero模塊完成了這些概念。前端

 

1、角色訪問控制之RBAC

  RBAC:Role Based Access Control,基於角色的訪問控制,這在目前大多數軟件中來說已經算得上是廣泛應用了,最多見的結構以下,結構簡單,設計思路清晰。java

  

  可是也存在其它升級版的設計,諸如用戶權限表、角色組、用戶組的概念等,具體分類有RBAC0、RBAC一、RBAC2等,後者功能愈來愈強大,也愈來愈複雜。git

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

 

2、ABP中的RBAC

  在Abp中,已經集成了這些概念,並在ModuleZero模塊中實現了這些概念,基於IdentityServer4的ModuleZero模塊完成了封裝。對於咱們大多數以業務爲中心的開發人員來說,不該該又去造一個輪子,而是應該開好這輛車。首先看下Abp中的RBAC模型bootstrap

  

  在這其中權限表中記錄了用戶與權限,角色與權限兩部分。對於權限一般指的是功能權限和數據權限兩部分,通常來說,大多指的是功能權限,這種經過角色與權限進行管理便可,如還有用戶部分的功能區分,則能夠再使用上用戶與權限,而對於數據權限,能夠利用用戶與權限部分,我的用的比較少,可是,能夠想象到這麼一個場景,針對於一家門店內的多個店長,角色相同即相應的權限相同,但各自關心的數據來源不一樣,關心東部、南部等數據,而不關心西部、北部數據,所以能夠在數據層面進行劃分,好比設置數據來源,東南西北,對於數據來源進行權限關聯,這樣一來用戶自己若是擁有東部數據權限,則只能看到東部數據。後端

 

一、權限聲明及應用mvc

  在Abp中,須要首先在Core層/Authorization/PermissionNames.cs中聲明權限,Abp權限部分設計原則是:先聲明再使用app

/// <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中設置具體權限,在此處設置權限時,能夠根據權限設計時候的職責劃分,好比若是僅僅是多租戶須要這部分,那便設置權限範圍爲多租戶便可。async

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當前模塊中,才能使得該部分權限設置生效。ide

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

   做爲業務的入口,菜單是較爲直觀的體現方式,如今能夠,爲菜單分配權限了,擁有權限的人才能看的到菜單,同時後臺方法中也要有權限斷定,菜單僅做爲前端入口上的控制,權限斷定做爲後端的控制。在MVC層的Startup/XXXNavigationProvider.cs中完成菜單的配置工做,能夠配置多級菜單,每一個菜單能夠配置相應的權限,在生成菜單斷定時,若是父級菜單權限不足,則直接會跳過子級菜單的斷定。佈局

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,望技術有成後能回來看見本身的腳步
相關文章
相關標籤/搜索