用裝飾器模式實現列表數據許可權

来源:https://www.cnblogs.com/lws66/archive/2019/07/15/11186560.html
-Advertisement-
Play Games

最近一直在重構系統,看到我們原來的代碼里,對於數據許可權的實現居然是在查詢語句里寫死的。 正感慨這祖傳代碼怎麼這麼坑,領導就讓我重新設計許可權模塊。這.... 好吧,反正都在重構代碼,直接推翻重來也不算填坑。 先開始梳理需求,所謂“數據許可權”,即經過普通的菜單、按鈕許可權後,對用戶能獲取到的數據再進行一次 ...


最近一直在重構系統,看到我們原來的代碼里,對於數據許可權的實現居然是在查詢語句里寫死的。

正感慨這祖傳代碼怎麼這麼坑,領導就讓我重新設計許可權模塊。這....

好吧,反正都在重構代碼,直接推翻重來也不算填坑。

 

先開始梳理需求,所謂“數據許可權”,即經過普通的菜單、按鈕許可權後,對用戶能獲取到的數據再進行一次許可權校驗。只顯示用戶有許可權訪問的數據。

經過一番思考我總結出了這個功能的幾個要點:

1.許可權針對用戶能看到的數據(看不到也就無從操作了),這些數據的來源都是列表查詢

2.這些列表查詢都含有需要校驗許可權的欄位

3.同一種需要校驗許可權的欄位存在不同的許可權校驗方式

分析完需求,我馬上想到了設計模式里的裝飾器模式。

什麼是裝飾器模式?

裝飾器模式就是當你要給一個對象“穿衣服”時,把衣服封裝起來。穿衣服的類不需要關心“衣服”的實現,只管穿。

這樣一來就可以在不修改類的情況下隨意增加或者重新設計“衣服”種類。

代入我們的場景,裝飾器模式的實現就變成了這樣:

我的query需要經過許可權校驗篩選出更少的數據,我們把許可權校驗篩選的操作封裝起來,再設計一個校驗引擎之類的方法來給query選擇該如何校驗。這樣一來原來寫好的query不需要改變。

query不需要改變是最關鍵的。整個系統那麼多列表查詢,如果許可權改變了就全都要修改(如原來的那種寫法),那簡直是災難。

 

簡單瞭解了什麼是裝飾器模式之後,我們再做一些準備工作就可以開始寫裝飾器了。

準備工作就是給我們的query寫一個介面,配合泛型使用,讓裝飾器知道傳進來的query一定含有需要校驗許可權的欄位

    public interface IAuthorityEntity
    {
        /// <summary>
        ///  管理部門
        /// </summary>
        /// <returns></returns>string MaintainDept { get; set; }
     }

有了這個介面,我們現在可以開始動手寫裝飾器了,首先我們定義一個裝飾器介面:

這裡使用泛型約束規定傳進來的query必須實現我們剛纔定義的IAuthorityEntiy介面。

所有的裝飾器都必須實現Filter方法,我們通過這個方法來將不滿足許可權的數據過濾掉。

    public interface IAuthorityComponent<T> where T : class,IAuthorityEntity
    {
        IQueryable<T> Filter(IQueryable<T> query,string key);
    }

所有裝飾器要實現這個介面,這樣我們的校驗引擎可以通過依賴註入的方式來獲取不同的裝飾器實現。

接下來我們先簡單實現幾個基於部門篩選的裝飾器:

    public abstract class AbstractDepartmentFilter<T> : IAuthorityComponent<T> where T : class,IAuthorityEntity
    {
        protected IOrganizationBLL organizationBLL;
        public virtual IQueryable<T> Filter(IQueryable<T> query, string departmentID)
        {
            query = query.Where(f => f.MaintainDept.Contains(departmentID));
            return query;
        }
    }

    /// <summary>
    /// 本部門
    /// </summary>
    public class DepartmentFilter<T> : AbstractDepartmentFilter<T> where T : class, IAuthorityEntity
    {
    }

    /// <summary>
    /// 指定部門
    /// </summary>
    public class DesignatedDepartmentFilter<T> : AbstractDepartmentFilter<T> where T : class, IAuthorityEntity
    {
        public override IQueryable<T> Filter(IQueryable<T> query, string deptID)
        {
            if (!string.IsNullOrWhiteSpace(deptID))
            {
                var keys = deptID.Split(',');
                query = query.Where(f => keys.Contains(f.MaintainDept));
                return query;
            }
            return query.Where(f => false);
        }
    }

    /// <summary>
    /// 本部門及子部門
    /// </summary>
    public class DepartmentAndChildDepartmentFilter<T> : AbstractDepartmentFilter<T> where T : class, IAuthorityEntity
    {
        ......
    }

好了,有了這些裝飾器,我們可以開始寫引擎來裝飾query了。

也是一樣先來個介面和抽象類:

    public interface IAuthority<T> where T : class, IAuthorityEntity
    {
        IQueryable<T> AuthorityFilter(IQueryable<T> query, EnumAccessScope accessScope, string key);
    }
    public abstract class AbstractAuthority<T> : IAuthority<T> where T : class, IAuthorityEntity
    {
        public virtual IQueryable<T> AuthorityFilter(IQueryable<T> query, EnumAccessScope accessScope, string key)
        {
            return AuthorityCore(query, accessScope, key);
        }

        protected virtual IQueryable<T> AuthorityCore(IQueryable<T> query, EnumAccessScope accessScope, string key)
        {
            throw new ExecutionException("該方法未實現");
        }
    }

接下來實現一個基於部門校驗的引擎:

    public class DepartmentAuthority<T> : AbstractAuthority<T> where T : class, IAuthorityEntity
    {
        IOrganizationBLL organizationBLL;

        public DepartmentAuthority(IOrganizationBLL organizationBLL)
        {
            this.organizationBLL = organizationBLL;
        }

        protected override IQueryable<T> AuthorityCore(IQueryable<T> query, EnumAccessScope accessScope, string key)
        {
            var deptQuery = query;
            switch (accessScope)
            {
                case EnumAccessScope.All: { break; }
                case EnumAccessScope.Department:
                    {
                        query = new DepartmentFilter<T>().Filter(deptQuery, key);
                        break;
                    }
                case EnumAccessScope.DepartmentAndChildDepartment:
                    {
                        query = new DepartmentAndChildDepartmentFilter<T>(organizationBLL).Filter(deptQuery, key);
                        break;
                    }
                case EnumAccessScope.DesignatedDepartment:
                    {
                        query = new DesignatedDepartmentFilter<T>().Filter(deptQuery, key);
                        break;
                    }
                default: { throw new Exception("許可權讀取錯誤"); }
            }
            return query;
        }
    }

如果以後新增了一個許可權是需要用到不止一種判斷(如指定部門+本部門),在case里多調一個或多個Filter即可實現“套餐”許可權。

 

寫完引擎之後,接下來就是在外面調用時選擇適合自己的許可權套餐了:

這裡跟我們系統的業務代碼相關性比較高,我就把一些邏輯省略了。

大體思路是註入合適版本的許可權引擎,然後將query,許可權和參數(如指定的部門ID)傳入引擎。

        protected IQueryable<T> Authority<T>(IQueryable<T> query, string controllerName) where T : class, IAuthorityEntity
        {
            var factoryKey = string.Empty;
            var accessScope = 0;
            //具體的許可權獲取和判斷邏輯省略
            //這裡使用autofac來註入許可權引擎
            var factory = AutofacConfig.container.Resolve<AuthorityFactory<T>>();
            var authority = factory.Classes[factoryKey];
            return authority.AuthorityFilter(query, accessScope, key);
        }

到這裡代碼其實已經寫完了。

感興趣的朋友們可以想想如果要添加一個基於用戶校驗要怎麼寫代碼。

 

如果有更好的寫法歡迎各位大神交流討論。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、mybatis逆向工程 由官方自動生成dao mapper.xml pojo等文件步驟:1)、導入jar包: mybatis-generator-core-1.3.6 代碼生成器的核心包 mysql-connector-java-5.1.28-bin.jar 連接資料庫 mybatis-3.2. ...
  • SQL映射器Mapper介面 MyBatis基於代理機制,可以讓我們無需再寫Dao的實現。直接把以前的dao介面定義成符合規則的Mapper。 註意事項: 1.介面必須以Mapper結尾,名字是DomainMapper 2.mapper.xml文件要和Mapper介面建立關係,通過namespace ...
  • 1.網上大部分都是這種方法 註釋掉 tomcat 9 安裝目錄下的conf里的 logging.properties 找到 java.util.logging.ConsoleHandler.encoding = UTF-8 將其註釋掉,或改為 GBK 2.第二種方法,修改JAVA預設語言 在大部分w ...
  • python預設參數陷阱 0|1陷阱? 學過函數的人一定聽說過函數的預設參數,關於函數的預設參數,請看以下的例子: def extendList(val, lst=[]): lst.append(val) return lst list1 = extendList(10) list2 = exten ...
  • 前言 之前我們探討過一個.class文件是如何被載入到jvm中的。但是jvm內又是如何劃分記憶體的呢?這個內被載入到了那一塊記憶體中?jvm記憶體劃分也是面試當中必被問到的一個面試題。 什麼是jvm記憶體區域劃分? 其實這個問題非常簡單,JVM在運行我們寫好的代碼時,他是必須使用多塊記憶體空間的,不同的記憶體空 ...
  • //表單@if($v['sex']==0) <td class="se" ss="{{$v['sex']}}" id="{{$v['id']}}" >男</td> @elseif($v['sex']==1) <td class="se" ss="{{$v['sex']}}" id="{{$v['id ...
  • 一、依賴註入的概念瞭解 介紹依賴註入(DI),首先要先瞭解一個概念——即控制反轉(IoC)。 控制反轉是面向對象編程的一種設計原則,可以用來減低電腦代碼之間的耦合度。在傳統的應用程式中,都是程式員手動在類的內部創建需要依賴的對象,而這種方式經常會導致類與類之間的高度耦合,難以測試。而當有了IoC容 ...
  • java高併發系列 第1天:必須知道的幾個概念 同步(Synchronous)和非同步(Asynchronous) 同步和非同步通常來形容一次方法調用, 同步方法調用一旦開始,調用者必須等到方法調用返回後,才能繼續後續的行為 。 非同步方法調用更像一個消息傳遞,一旦開始,方法調用就會立即返回,調用者就可以 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...