1. 程式人生 > >ABP入門系列(9)——許可權管理

ABP入門系列(9)——許可權管理

完成了簡單的增刪改查和分頁功能,是不是覺得少了點什麼?
是的,少了許可權管理。既然涉及到了許可權,那我們就細化下任務清單的功能點:

  • 登入的使用者才能檢視任務清單
  • 使用者可以無限建立任務並分配給自己,但只能對自己建立的任務進行查詢、修改
  • 管理員可以建立任務並分配給他人
  • 管理員具有刪除任務的許可權

從以上的資訊中,我們可以提取出以下許可權:

  1. 任務分配許可權
  2. 任務刪除許可權

那我們下面就來實現針對這兩個許可權的管理:

一、ABP許可權管理的實現

1、先來看看許可權定義相關型別:

許可權定義及獲取相關型別依賴圖

從該型別依賴圖中我們可以看出:

  • Permission:許可權類,定義了許可權的屬性。
  • PermissionDictionary
    :繼承自Dictionary<string, Permission>類,儲存permission物件的字典。
  • IPermisssionDefinitionContext:定義了CreatePermissionGetPermissionOrNull方法,分別用來建立和獲取許可權。
  • AuthorizationProvider:抽象類,在Module中實現該介面來定義許可權。
  • PermissionManager:許可權管理類,繼承自PermissionDefinitionContextBase主要提供了獲取許可權的系列方法。

2、再來看看許可權檢查相關型別

許可權檢查相關型別依賴圖

從該型別依賴圖中簡要梳理下核心類:

  • IPermissionChecker:從介面命名就明白,這個是用來進行許可權檢查的。我們可以自己實現它,也可以使用module-zero中給出的實現。
  • NullPermissionChecker:當未實現IPermissionChecker,系統會預設使用此類將許可權賦予給每個使用者。
  • AbpAuthorizeAttribute:許可權檢查特性,在應用服務層標註需要的許可權。
  • AbpAllowAnonymousAttribute:匿名訪問特性,忽略許可權檢查,用於應用服務層。在mvc和webapi中使用[AllowAnonymous]
  • AuthorizationInterceptor:授權攔截器,用來攔截定義了AbpAuthorizeAttribute
    特性的方法。

核心的幾個類就講到這裡,具體的實現,可以自行檢視原始碼一探究竟。

二、定義許可權

從上節中我們知道在不同的Module中通過繼承AuthorizationProvider來定義許可權。ABP模板專案中已經在領域層,也就是.Core結尾的專案中,定義了xxxxxxAuthorizationProvider類繼承自AuthorizationProvider

1、許可權包含哪些屬性

  • Name:系統中 唯一的名字。最好為許可權的名字定義一個const字串而不是變數字串。我們偏向使用“.”符號用於有層次的名字,但這不是強制的。你可以設定任何你喜歡的名字,唯一的一點是保證它必須是唯一的。
  • DisplayName:用於以後在UI上顯示許可權的本地化字串。
  • Description:用於以後在UI上顯示許可權定義的本地化字串。
  • IsGrantedByDefault:表示該許可權是否授予給所有登入的使用者,除非該許可權顯式禁止未授予給使用者。該值一般預設為false。
  • MultiTenancySides:對於多租戶應用,租戶或者租主可以使用同一個許可權。這是一個Flags列舉,因此一個許可權可以用於租戶和租主。
  • featureDependency:可以用於宣告一個功能的依賴。因此,只有功能依賴滿足了,該許可權才會被授予。

2、定義任務分配和任務刪除許可權

ABP模板專案預設已經在.Core/Authorization/目錄下建立了AuthorizationProvider的派生類xxxxAuthorizationProvider.cs
其中程式碼為:

public override void SetPermissions(IPermissionDefinitionContext context)
{
    //Common permissions
    var pages = context.GetPermissionOrNull(PermissionNames.Pages);
    if (pages == null)
        pages = context.CreatePermission(PermissionNames.Pages, L("Pages"));

    var users = pages.CreateChildPermission(PermissionNames.Pages_Users, L("Users"));

    //Host permissions
    var tenants = pages.CreateChildPermission(PermissionNames.Pages_Tenants, L("Tenants"),
        multiTenancySides: MultiTenancySides.Host);
}

可以看出主要添加了三個許可權,Pages、Users、Tenants。
依葫蘆畫瓢,咱們建立一個TaskAuthorizationProvider繼承自AuthorizationProvider
程式碼如下:

public class TaskAuthorizationProvider : AuthorizationProvider
{
    public override void SetPermissions(IPermissionDefinitionContext context)
    {
        //Common permissions
        var pages = context.GetPermissionOrNull(PermissionNames.Pages);
        if (pages == null)
            pages = context.CreatePermission(PermissionNames.Pages, L("Pages"));

        //Tasks
        var tasks = pages.CreateChildPermission(PermissionNames.Pages_Tasks, L("Tasks"));
        tasks.CreateChildPermission(PermissionNames.Pages_Tasks_AssignPerson, L("AssignTaskToPerson"));
        tasks.CreateChildPermission(PermissionNames.Pages_Tasks_Delete, L("DeleteTask"));
    }

    private static ILocalizableString L(string name)
    {
        return new LocalizableString(name, LearningMpaAbpConsts.LocalizationSourceName);
    }
}

並在常量PermissionNames類中維護唯一使用者名稱。

public const string Pages_Tasks = "Pages.Pages.Tasks";

public const string Pages_Tasks_AssignPerson = "Pages.Pages.Tasks.AssignPerson";

public const string Pages_Tasks_Delete = "Pages.Pages.Tasks.Delete";

需要本地化顯示的,則需要分別維護本地化xml檔案,這裡忽略此步。

定位到.Core/xxxCoreModule.cs檔案中發現Abp已經為預設實現的的xxxxAuthorizationProvider.cs註冊了。
Configuration.Authorization.Providers.Add<LearningMpaAbpAuthorizationProvider>();
因為ABP是模組化的,當你需要為自己自定義的模組定義許可權時,
不要忘記在自己定義的Module中註冊自己實現的AuthorizationProvider(授權提供器)。
所以,還是依葫蘆畫瓢,註冊TaskAuthorizationProvider
Configuration.Authorization.Providers.Add<TaskAuthorizationProvider>();

三、許可權檢查

在應用服務層中直接使用[AbpAuthorize]特性,但在MVC控制器中使用[AbpMvcAuthorize]特性,Web API控制器中使用[AbpApiAuthorize]

我們在應用服務層給刪除操作定義許可權檢查:

[AbpAuthorize(PermissionNames.Pages_Tasks_Delete)]
public void DeleteTask(int taskId)
{
    var task = _taskRepository.Get(taskId);
    if (task != null)
        _taskRepository.Delete(task);
}

F5執行,去刪除某一任務,將獲得以下提示:

未授權提示

2、使用IPermissionChecker

刪除任務是一個獨立的操作,所以我們可以直接使用上面特性宣告的方式來進行許可權檢查。
但是針對【任務分配】這個操作,它其實是任務建立、編輯中的一個子操作。所以我們不能直接使用特性宣告的方式來進行許可權檢查。這一次我們使用IPermissionChecker來進行檢查。

2.1、應用服務層注入了PermissionChecker屬性
因為授權一般在應用服務層中進行,所以ABP預設在ApplicationService基類注入並定義了PermissionChecker屬性。這樣,在應用服務層就可以直接使用PermissionChecker屬性進行許可權檢查。
2.2、修改建立任務方法,加入許可權檢查:

public int CreateTask(CreateTaskInput input)
{
    //We can use Logger, it's defined in ApplicationService class.
    Logger.Info("Creating a task for input: " + input);

    //獲取當前使用者
    var currentUser = AsyncHelper.RunSync(this.GetCurrentUserAsync);

    //判斷使用者是否有許可權
    if (input.AssignedPersonId.HasValue && input.AssignedPersonId.Value != currentUser.Id)
        PermissionChecker.Authorize(PermissionNames.Pages_Tasks_AssignPerson);

    var task = Mapper.Map<Task>(input);

    int result = _taskRepository.InsertAndGetId(task);

    //只有建立成功才傳送郵件和通知
    if (result > 0)
    {
        task.CreationTime = Clock.Now;

        if (input.AssignedPersonId.HasValue)
        {
            task.AssignedPerson = _userRepository.Load(input.AssignedPersonId.Value);
            var message = "You hava been assigned one task into your todo list.";

            //TODO:需要重新配置QQ郵箱密碼
            //SmtpEmailSender emailSender = new SmtpEmailSender(_smtpEmialSenderConfig);
            //emailSender.Send("[email protected]", task.AssignedPerson.EmailAddress, "New Todo item", message);

            _notificationPublisher.Publish("NewTask", new MessageNotificationData(message), null,
                NotificationSeverity.Info, new[] { task.AssignedPerson.ToUserIdentifier() });
        }
    }

其中許可權檢查程式碼為:PermissionChecker.Authorize(PermissionNames.Pages_Tasks_AssignPerson);這種方式當沒有許可權時,將直接丟擲異常。當啟用<customErrors mode="On" />時,將跳轉至Error檢視並顯示以下資訊。
授權提示

2.3、修改編輯任務方法,加入許可權檢查:

public void UpdateTask(UpdateTaskInput input)
{
    //We can use Logger, it's defined in ApplicationService base class.
    Logger.Info("Updating a task for input: " + input);

    //獲取當前使用者
    var currentUser = AsyncHelper.RunSync(this.GetCurrentUserAsync);
    //獲取是否有許可權
    bool canAssignTaskToOther = PermissionChecker.IsGranted(PermissionNames.Pages_Tasks_AssignPerson);
    //如果任務已經分配且未分配給自己,且不具有分配任務許可權,則丟擲異常
    if (input.AssignedPersonId.HasValue && input.AssignedPersonId.Value != currentUser.Id && !canAssignTaskToOther)
    {
        throw new AbpAuthorizationException("沒有分配任務給他人的許可權!");
    }

    var updateTask = Mapper.Map<Task>(input);
    _taskRepository.Update(updateTask);
}

其中許可權檢查程式碼為PermissionChecker.IsGranted(PermissionNames.Pages_Tasks_AssignPerson);IsGranted()方法返回true or false。
許可權提示

2.4、Razor頁面如何進行許可權檢查
檢視基類定義了IsGranted方法來檢查當前使用者是否具有許可權。我們可以在_List.cshtml.cs中加入以下程式碼來控制是否顯示刪除按鈕。

@if (IsGranted(PermissionNames.Pages_Tasks_Delete))
{
    <button type="button" class="btn btn-success" onclick="deleteTask(@task.Id);">Delete</button>
}

2.5、js程式碼如何進行許可權檢查
abp.auth名稱空間下定義了許可權相關的API,在js中我們可以直接使用。
這裡不再舉例。

四、將新增的許可權賦予給Admin

完成了許可權的定義和檢查,我們如何進行許可權設定呢,如何為角色或使用者賦予許可權呢?
在ABP模板專案中暫未提供使用者角色許可權管理功能,但在AbpZero中提供了該功能,支援按使用者或角色賦予許可權。那咋辦呢?
咱們退而求其次,在資料庫初始化的時候,將許可權賦給Admin。
但是我們的資料庫已經建立好了啊?
反正是測試庫,刪掉重建唄。

1、刪除資料庫

怎麼刪資料庫,自己應該知道吧。

2、程式碼中為Admin賦予許可權

開啟基礎設施層,即以EntityFramework結尾的專案中,定位到Migrations\SeedData資料夾,分別在
HostRoleAndUserCreatorTenantRoleAndUserBuilder兩個類中新增以下程式碼:

var taskPermissions = PermissionFinder.GetAllPermissions(new PersonAppAuthorizationProvider()).ToList();
permissions.AddRange(taskPermissions);

3、重新編譯整個專案,執行Update-Database

檢視資料庫,發現已經將Permission賦予給了admin

總結:

本節主要講解了ABP許可權管理的基本實現方式,以及如何定義、使用和新增許可權。

在ABP模板專案中暫未提供使用者角色許可權管理功能,但在AbpZero中提供了該功能,支援按使用者或角色賦予許可權。這一節先暫時不表,等我研究通徹了再和大家娓娓道來。

遺留問題:

  1. 在模態框上如何彈出異常資訊?

相關推薦

ABP入門系列9——許可權管理

完成了簡單的增刪改查和分頁功能,是不是覺得少了點什麼? 是的,少了許可權管理。既然涉及到了許可權,那我們就細化下任務清單的功能點: 登入的使用者才能檢視任務清單 使用者可以無限建立任務並分配給自己,但只能對自己建立的任務進行查詢、修改 管理員可以建立任務並分配給他人 管理員具有刪除任務的許可權 從以上

arcgis jsapi介面入門系列9:可以同時顯示多個的地圖popup

jsapi有提供popup功能,但缺點很多,例如地圖上只能同時顯示一個popup,popup內容有限制等 本文提供另一個方法,原理不用jsapi,在地圖外用一個普通的div放在地圖上面,再監聽地圖的滑鼠移動等時間控制這div跟著地圖聯動 本文程式碼可能存在跟框架的css等繫結,不一定能直接執

ABP入門系列12——如何升級Abp並除錯原始碼

1. 升級Abp 本系列教程是基於Abp V1.0版本,現在Abp版本已經升級至V1.4.2(截至至文章釋出時間),其中新增了New Feature,並對Abp做了相應的Enhancements,以及Bug fixs。現在我們就把它升級至最新版本,那如何升級呢? 下面就請按我的步驟來將Abp由V1.0升級

ABP入門系列16——通過webapi與系統進行互動

1. 引言 上一節我們講解了如何建立微信公眾號模組,這一節我們就繼續跟進,來講一講公眾號模組如何與系統進行互動。 微信公眾號模組作為一個獨立的web模組部署,要想與現有的【任務清單】進行互動,我們要想明白以下幾個問題: 如何進行互動? ABP模板專案中預設建立了webapi專案,其動態webapi技術允

ABP入門系列14——應用BootstrapTable表格外掛

1. 引言 之前的文章ABP入門系列(7)——分頁實現講解了如何進行分頁展示,但其分頁展示僅適用於前臺web分頁,在後臺管理系統中並不適用。後臺管理系統中的資料展示一般都是使用一些表格外掛來完成的。這一節我們就使用BootstrapTable進行舉例說明。 2. BootstrapTable 基於 B

ABP入門系列6——定義導航選單

完成了增刪改查以及頁面展示,這一節我們來為任務清單新增【導航選單】。 在以往的專案中,大家可能會手動在layout頁面中新增一個a標籤來新增導航選單,這也是一種方式,但是如果要針對不同使用者不同許可權決定是否顯示某個選單,那麼直接在layout頁面中去控制就不方便了。 不過,ABP已經為大家考慮了這一點,集

ABP入門系列8——Json格式化

講完了分頁功能,這一節我們先不急著實現新的功能。來簡要介紹下Abp中Json的用法。為什麼要在這一節講呢?當然是做鋪墊啊,後面的系列文章會經常和Json這個東西打交道。 一、Json是幹什麼的 JSON(JavaScript Object Notation) 是一種輕量級的資料交換格式。 易於人閱讀和編寫

ABP入門系列19——使用領域事件

1.引言 最近剛學習了下DDD中領域事件的理論知識,總的來說領域事件主要有兩個作用,一是解耦,二是使用領域事件進行事務的拆分,通過引入事件儲存,來實現資料的最終一致性。若想了解DDD中領域事件的概念,可參考DDD理論學習系列(9)-- 領域事件。 Abp中使用事件匯流排來實現領域事件,而關於事件匯流排的

ABP入門系列5——展現層實現增刪改查

這一章節將通過完善Controller、View、ViewModel,來實現展現層的增刪改查。最終實現效果如下圖: 一、定義Controller ABP對ASP.NET MVC Controllers進行了整合,通過引入Abp.Web.Mvc名稱空間,建立Controller繼承自AbpControlle

ABP入門系列18—— 使用領域服務

1.引言 自上次更新有一個多月了,發現越往下寫,越不知如何去寫。特別是當遇到DDD中一些概念術語的時候,尤其迷惑。如果只是簡單的去介紹如何去使用ABP,我只需參照官方文件,實現到任務清單Demo中去就可以了,不勞神不費力。但是,這樣就等於一知半解。 知之為知之,不知為不知,是知也。知其然知其所以然,方能舉

ABP入門系列2——領域層建立實體

這一節我們主要和領域層打交道。首先我們要對ABP的體系結構以及從模板建立的解決方案進行一一對應。網上有程式碼生成器去簡化我們這一步的任務,但是不建議初學者去使用。 一、首先來看看ABP體系結構 領域層就是業務層,是一個專案的核心,所有業務規則都應該在領域層實現。 實體(Entity): 實體代表業務領域的

ABP入門系列4——建立應用服務

一、解釋下應用服務層 應用服務用於將領域(業務)邏輯暴露給展現層。展現層通過傳入DTO(資料傳輸物件)引數來呼叫應用服務,而應用服務通過領域物件來執行相應的業務邏輯並且將DTO返回給展現層。因此,展現層和領域層將被完全隔離開來。 以下幾點,在建立應用服務時需要注意: 在ABP中,一個應用服務需要實現IAp

ABP入門系列11——編寫單元測試

1. 前言 In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program

ABP入門系列15——建立微信公眾號模組

1. 引言 現在的網際網路已不在僅僅侷限於網頁應用,IOS、Android、平板、智慧家居等平臺正如火如荼的迅速發展,移動應用的需求也空前旺盛。所有的網際網路公司都不想錯過這一次移動浪潮,佈局移動市場分一份移動紅利。 的確,智慧手機作為我們日常生活已必不可少的一部分,通過手機app能夠獲得更好的體驗,比如

ABP入門系列3——領域層定義倉儲並實現

一、先來介紹下倉儲 倉儲(Repository): 倉儲用來操作資料庫進行資料存取。倉儲介面在領域層定義,而倉儲的實現類應該寫在基礎設施層。 在ABP中,倉儲類要實現IRepository介面,介面定義了常用的增刪改查以及聚合方法,其中包括同步及非同步方法。主要包括以下方法: ABP針對不同的ORM框架對

ABP入門系列1——通過模板建立MAP版本專案

一、從官網建立模板專案 依次按下圖選擇: 輸入驗證碼開始下載 下載提示: 二、啟動專案 使用VS2015開啟專案,還原Nuget包: 設定以Web結尾的專案,設定為啟動專案: 開啟Web.config,修改連線字串。(因為我本地裝的sqlserver是例項是.sqlexpress,所以需要

ABP入門系列13——Redis快取用起來

1. 引言 建立任務時我們需要指定分配給誰,Demo中我們使用一個下拉列表用來顯示當前系統的所有使用者,以供使用者選擇。我們每建立一個任務時都要去資料庫取一次使用者列表,然後繫結到使用者下拉列表顯示。如果就單單對一個demo來說,這樣實現也無可厚非,但是在正式專案中,顯然是不合理的,浪費程式效能,有待優化

ABP入門系列7——分頁實現

完成了任務清單的增刪改查,咱們來講一講必不可少的的分頁功能。 首先很慶幸ABP已經幫我們封裝了分頁實現,實在是貼心啊。 來來來,這一節咱們就來捋一捋如何使用ABP的進行分頁吧。 一、分頁請求DTO定義 資料傳輸物件(Data Transfer Objects)用於應用層和展現層的資料傳輸。 展現層傳入資料

ABP入門系列17——使用ABP整合的郵件系統傳送郵件

ABP中對郵件的封裝主要整合在Abp.Net.Mail和Abp.Net.Mail.Smtp名稱空間下,相應原始碼在此。 #一、Abp整合的郵件模組是如何實現的 分析可以看出主要由以下幾個核心類組成: EmailSettingNames:靜態常量類,主要定義了傳送郵件需要的相關引數:Port、Host、Us

ASP.NET AJAX入門系列9:在母版頁中使用UpdatePanel

{    switch (((Control)sender).ID)    {        case"IncrementButton":            this.Offset =this.Offset +1;            break;        case"DecrementButton