在我的很多Winform開發項目中(包括混合框架的項目),統一采用了權限管理模塊來進行各種權限的控制,包括常規的功能權限(按鈕、菜單權限)、數據權限(記錄的權限),另外還可以進行字段級別的字段權限控制,字段權限是我們在一些對權限要求比較嚴格的系統里面涉及到的,可以對部分用戶隱藏一些敏感的信息。本篇主要介紹字段權限的控制思路及實現機制,以便大家對這個字段權限的控制有一個直觀的了解。
如果需要對權限系統的功能進行一定的了解,可以先回顧下我前面的文章《Winform開發框架之權限管理系統功能介紹》、《如何在應用系統中實現數據權限的控制功能》、《如何在應用系統中實現數據權限的控制功能(2)》,以及《Winform開發框架之權限管理系統改進的經驗總結(1)-TreeListLookupEdit控件的使用》、《Winform開發框架之權限管理系統改進的經驗總結(2)-用戶選擇界面的設計》、《Winform開發框架之權限管理系統改進的經驗總結(4)--用戶分級管理》等文章。
1、字段權限的設計
字段的權限控制,一般就是控制對應角色人員的對某個表的一些敏感字段的可訪問性:包括可見、可編輯性等處理。
在設計字段權限的時候,我們需要了解這些還是基於RBAC的概念,基於角色進行授權的,而且我們的字段列表是屬於具體的業務對象列表的,這里的業務對象是指一些我們具體的業務模塊,如客戶基礎信息、人員基礎信息、報價單等等,我們就是基於這些業務進行字段的控制的。
如下界面所示,我們在權限系統里面也可以對其字段進行權限控制,如下圖所示。先選擇左邊的具體角色,然后添加一些業務對象,並設置它們的權限即可。
首次業務對象需要用戶加入,這里以程序集中的實體類進行字段信息的標識處理,如下所示可以加載對應業務信息。
我們在業務對象列表的【顯示設置】處可以單擊旁邊的按鈕,在彈出的界面上進行條件的設置,如下界面效果所示。
這樣我們就完成了對某個業務對象的各個字段進行配置了,具體的字段控制在業務模塊里面添加部分代碼即可實現了。
例如我們以系統黑名單為例介紹,通過上面的方式進行設置,隱藏起始和結束IP地址的字段,那么列表界面得到的效果如下所示。
同時,如果系統界面有新增或者編輯界面,那么我們也需要隱藏才可以達到效果,如下是其的編輯界面效果(隱藏顯示那兩個字段了)。
以上就是整個字段權限控制的設計思路和實現了,但是具體我們是如何在業務模塊里面整合這些權限控制呢?下面我們進行介紹。
2、字段權限的列表控制處理
前面我們介紹了在權限系統中進行業務對象的字段權限的設置流程,以及以其中的【登陸系統黑白名單】的業務模塊進行的演示,那么我們如何才能在自己的業務模塊里面進行控制處理的呢?
首先我們需要在業務列表綁定的時候,需要獲取我們當前用戶能夠訪問的字段列表,默認是全部可見,但是如果用戶設置了條件,那么就需要獲取對應的權限列表進行控制了,具體的控制代碼如下所示。
//根據業務對象獲取對應的顯示字段,如果沒有設置,那么根據FieldPermit表的配置獲取字段權限列表 var permitDict = BLLFactory<FieldPermit>.Instance.GetColumnsPermit(typeof(BlackIPInfo).FullName, Portal.gc.UserInfo.ID); var displayColumns = BLLFactory<BlackIP>.Instance.GetDisplayColumns(); displayColumns = string.IsNullOrEmpty(displayColumns) ? string.Join(",", permitDict.Keys) : displayColumns; this.winGridViewPager1.DisplayColumns = displayColumns;
然后在設置字段的中文映射顯示
//設置字段的中文顯示 this.winGridViewPager1.ColumnNameAlias = BLLFactory<BlackIP>.Instance.GetColumnNameAlias();//字段列顯示名稱轉義
在對列表進行數據綁定后,我們統一設置各個字段的權限的可讀寫、可見或隱藏值權限即可,如下代碼所示。
//獲取字段顯示權限,並設置 this.winGridViewPager1.gridView1.SetColumnsPermit(permitDict);
整個數據綁定的代碼如下所示,這些代碼可以利用代碼生成工具Database2Sharp進行界面代碼統一生成。
/// <summary> /// 綁定列表數據 /// </summary> private void BindData() { //entity //根據業務對象獲取對應的顯示字段,如果沒有設置,那么根據FieldPermit表的配置獲取字段權限列表 var permitDict = BLLFactory<FieldPermit>.Instance.GetColumnsPermit(typeof(BlackIPInfo).FullName, Portal.gc.UserInfo.ID); var displayColumns = BLLFactory<BlackIP>.Instance.GetDisplayColumns(); displayColumns = string.IsNullOrEmpty(displayColumns) ? string.Join(",", permitDict.Keys) : displayColumns; this.winGridViewPager1.DisplayColumns = displayColumns; //設置字段的中文顯示 this.winGridViewPager1.ColumnNameAlias = BLLFactory<BlackIP>.Instance.GetColumnNameAlias();//字段列顯示名稱轉義 string where = GetConditionSql(); List<BlackIPInfo> list = BLLFactory<BlackIP>.Instance.Find(where); this.winGridViewPager1.DataSource = new WHC.Pager.WinControl.SortableBindingList<BlackIPInfo>(list); this.winGridViewPager1.PrintTitle = "登陸系統的黑白名單列表報表"; //獲取字段顯示權限,並設置 this.winGridViewPager1.gridView1.SetColumnsPermit(permitDict); }
對於DevExpress的GridControl列表控件,我們一般在處理過程中需要設置字段的DisplayText轉義,那么這種設置后,通過上面代碼處理的權限就會失效,我們可以利用對Tag的標識的判斷進行處理,如下所示,這樣就避免了權限控制無效的情況。
void gridView1_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e) { //如果字段權限不夠,那么字段的標簽設置為*的 if (string.Concat(e.Column.Tag) != "*") { if (e.Column.ColumnType == typeof(DateTime)) { string columnName = e.Column.FieldName; if (e.Value != null) { if (Convert.ToDateTime(e.Value) <= Convert.ToDateTime("1900-1-1")) { e.DisplayText = ""; } else { e.DisplayText = Convert.ToDateTime(e.Value).ToString("yyyy-MM-dd HH:mm");//yyyy-MM-dd } } } else if (e.Column.FieldName == "AuthorizeType") { if (e.Value != null) { e.DisplayText = ((AuthrizeType)e.Value).ToString(); } } } }
3、字段權限的顯示窗體控制處理
如果在開發Winform界面的時候,把列表的展示統一放在GridControl里面進行展示,不再獨立設計展示窗體,那么上面列表控制就已經達到了字段權限的控制目的了:可見或不可見、可編輯或只讀、顯示或隱藏值等處理。
在我的Winform框架中,我一般傾向於設計一個界面來展示業務對象的內容,一般新增,查看或者編輯都放在這個窗體上展示信息,比較直觀,那么這種對字段權限的控制也需要延伸到這個顯示窗體上;
對於普通的編輯控件,我們只能控制控件的可讀寫、可見與否的處理。
首先我們設計一個函數,用來設置控件的權限的,如下所示。
/// <summary> /// 設置控件字段的權限顯示或者隱藏 /// </summary> private void SetPermit() { #region 設置控件和字段的對應關系 this.txtName.Tag = "Name"; this.txtAuthorizeType.Tag = "AuthorizeType"; this.txtForbid.Tag = "Forbid"; this.txtIPStart.Tag = "IPStart"; this.txtIPEnd.Tag = "IPEnd"; this.txtNote.Tag = "Note"; this.txtCreator.Tag = "Creator"; this.txtCreateTime.Tag = "CreateTime"; #endregion //獲取列表權限的列表 var permitDict = CallerFactory<IFieldPermitService>.Instance.GetColumnsPermit(typeof(BlackIPInfo).FullName, Portal.gc.UserInfo.ID); this.SetControlPermit(permitDict, this.layoutControl1); }
在上面,我們的邏輯就是先為每個控件綁定一個字段的標識,最后通過獲取用戶的字段權限列表,對控件的權限進行統一的控制處理即可。
為了開發的省時省力,這些代碼可以利用代碼生成工具Database2Sharp進行界面代碼統一生成。
完成上面SetPermit函數的處理,我們在窗體界面的顯示內容上,最后統一設置控件的權限即可,如下代碼所示。
/// <summary> /// 數據顯示的函數 /// </summary> public override void DisplayData() { InitDictItem();//數據字典加載(公用) if (!string.IsNullOrEmpty(ID)) { #region 顯示信息 BlackIPInfo info = CallerFactory<IBlackIPService>.Instance.FindByID(ID); if (info != null) { tempInfo = info;//重新給臨時對象賦值,使之指向存在的記錄對象 txtName.Text = info.Name; txtAuthorizeType.SetComboBoxItem(info.AuthorizeType.ToString()); txtForbid.Checked = info.Forbid; txtIPStart.Text = info.IPStart; txtIPEnd.Text = info.IPEnd; txtNote.Text = info.Note; txtCreator.Text = info.Creator; txtCreateTime.SetDateTime(info.CreateTime); } #endregion //this.btnOK.Enabled = Portal.gc.HasFunction("BlackIP/Edit"); } else { txtCreator.Text = Portal.gc.UserInfo.FullName;//默認為當前登錄用戶 txtCreateTime.DateTime = DateTime.Now; //默認當前時間 //this.btnOK.Enabled = Portal.gc.HasFunction("BlackIP/Add"); } RefreshUsers(); SetPermit(); }
以上就是字段權限的設計思路,實現控制過程,這樣我們在權限里面實現了功能權限、菜單權限、數據記錄權限、字段權限的綜合控制,基本上能夠滿足大多數業務規則的要求了,從而提高了權限管理系統在整個應用開發中的通用性、便利性,一致性。