前一篇文章《通用權限管理設計 之 數據庫設計方案》介紹了【主體】- 【領域】 - 【權限】( who、what、how問題原型 ) 的設計思想html
本文將對這種設計思想做進一步的擴展,介紹數據權限的設計方案。前端
權限控制能夠理解,分爲這幾種 :web
【功能權限】:能作什麼的問題,如增長產品。
【數據權限】:能看到哪些數據的問題,如查看本人的全部訂單。
【字段權限】:能看到哪些信息的問題,如供應商帳戶,看不到角色、 部門等信息。sql
上面提到的那種設計就是【功能權限】,這種設計有必定的侷限性,對於主體,只能明確地指定。對於不明確的,在這裏可能就沒辦法處理。好比下面這幾種狀況:數據庫
數據僅當前部門及上級可見
數據僅當前用戶(本人)可見安全
相似這樣的就須要用到上面提的數據權限。服務器
上一篇文章我用一個表五個字段完成了【功能權限】的設計思路
本文我將介紹如何利用一個表兩個字段完成這個【數據權限】的設計思路數據結構
【數據權限】是在【功能權限】的基礎上面進一步的擴展,好比能夠查看訂單屬於【功能權限】的範圍,可是能夠查看哪些訂單就是【數據權限】的工做了。less
在設計中,咱們規定好若是沒有設置了數據權限規則,那麼視爲容許查看所有的數據。數據庫設計
幾個概念
【資源】:數據權限的控制對象,業務系統中的各類資源。好比訂單單據、銷售單等。屬於上面提到的【領域】中的一種
【主體】:用戶、部門、角色等。
【條件規則】:用於檢索數據的條件定義
【數據規則】:用於【數據權限】的條件規則
應用場景
1,訂單,能夠由本人查看
2,銷售單,能夠由本人或上級領導查看
3,銷售單,銷售人員能夠查看本身的,銷售經理只查看 銷售金額大於100,000的。
咱們能想到直接的方法,在訪問數據的入口加入SQL Where條件來實現,組織sql語句:
1,where UserID = {CurrentUserID}
2,where UserID = {CurrentUserID} or {CurrentUserID} in (領導)
3,where UserID = {CurrentUserID} or ({CurrentUserID} in (銷售經理) and 銷售金額 > 100000)
這些一個一個的"條件",本文簡單理解爲一個【數據規則】,一般會與原來咱們前臺的業務過濾條件合併再檢索出數據。
這是一種最直接的實現方式,在【資源】上面加一個【數據規則】(好比上面的三點)。這樣設計就是
【資源】 - 【數據規則】
我又以爲不一樣的人應該對應不一樣的規則,那麼也能夠理解爲,一個用戶對應不一樣的角色,每個角色有不同的【數據規則】,那麼設計就變成
【資源】 - 【主體】 - 【數據規則】
根據提供者的不一樣,準備不一樣的權限應對策略。
這裏能夠簡單地介紹一下,這個方案至少須要2張表,一個是 【資源,主體,規則關係表】、一個是【數據規則表】
關係表不能直接保存角色,由於你不肯定何時業務須要按照【部門】或者【分公司】來定義數據規則
因而能夠用 Master、MasterKey 相似這樣的兩個字段來肯定一個【主體】
用XML方式的話是這樣配置的(放在數據庫也相似):
<?xml version="1.0" encoding="utf-8"?>
<settings>
<rule view="訂單" role="銷售人員">
銷售員 = {CurrentUserID}
</rule>
<rule view="訂單" role="總銷售經理">
銷售金額 > 100000
</rule>
<rule view="訂單" role="區域銷售經理">
銷售金額 > 100000 and 區域 = {當前用戶所屬區域}
</rule>
</settings>
對於這種方式有興趣的朋友也能夠試一下,兩種方式的【數據規則】是同樣的,可是本文沒有采用第二種設計方式,由於它多了一層處理邏輯,我覺得應該設計越簡單越好,就採用第一種方式:
【資源】 - 【數據規則】
固然,上面是用SQL的方式來肯定條件規則的,咱們固然不會這麼作。SQL雖然靈活,可是咱們很難去維護,也不知道SQL在咱們的界面UI上面如何體現。難不成直接用一個文本框來顯示。這樣對應一個開發人員來講不是問題,但是對應系統管理員,很容易出問題。因此咱們須要有另外一方式來肯定這一規則,並最終能夠轉換成咱們的SQL語句。
咱們的設計關鍵在於如何規範好這些【數據規則】 ,這個規則必須是對前端友好的,並且是對後臺友好的,JSON顯然是很好的方式。
規則說明:
1,數據權限規則老是:{屬性 條件 容許值}
2,數據權限規則能夠合併。好比 ( {當前用戶 屬於 銷售人員} and {訂單銷售員 等於 當前用戶} ) Or {當前用戶 屬於 銷售經理}
3,最終咱們會用JSON格式
在檢索數據時會先判斷有沒有註冊了某某【資源】的【條件規則】,若是有,那麼加載這個【條件規則】併合併到咱們前臺的【搜索條件】(你的業務界面應該有一個搜索框吧)
以下圖定義了客戶信息的搜索框,咱們搜索客戶ID包括AN,咱們組織成的規則將會是:
{"rules":[{"field":"CustomerID","op":"like","value":"AN","type":"string"}],"op":"and"}
爲了更好地理解【數據規則】,這裏介紹一下【通用查詢機制】
權限控制總離不開一些條件的限制(好比查看當前部門的單據),若是沒有完善的通用查詢規則機制,那麼在作權限條件過濾的時候你會以爲很彆扭。這裏介紹一個通用查詢方案,而後再介紹如何實現【數據規則】。
早些時候我寫過一篇關於ligerGrid結合.net設計通用處理類的文章《 jQuery liger ui ligerGrid 打造通用的分頁排序查詢表格(提供下載) 》。裏面提到的過濾信息是直接的SQL語句。這是不可靠,並且不安全的。
在前端傳輸給後臺的過濾信息不該該是直接的SQL,而應該是一組過濾規則。在ligerui V1.1.8 已經加入了一個條件過濾器插件,這個插件組成的規則數據纔是我受推薦的:
好比以下
{"rules":
[
{"field":"OrderDate","op":"less","value":"2012-01-01"},
{"field":"CustomerID","op":"equal","value":"VINET"}
]
,"op":"and"}
規則描述:
查找顧客VINET全部訂單時間小於2011-01-01的單據
這樣的數據是安全的,並且是通用的(你甚至能夠再加一個OR子查詢)。不管是在前端仍是後臺,不管你使用什麼樣的組件,均可以很好地利用。
通用後臺的翻譯,就能夠生成這樣SQL的參數:
Text:
([OrderDate] < @p1 and [CustomerID] = @p2)
Parameters:
p1:2012-01-01
p2:VINET
下面來點複雜的:查找 顧客VINET或者TOMSP,全部訂單時間小於2011-01-01的單據
{
"rules":[{"field":"OrderDate","op":"less","value":"2012-01-01"}],
"groups":[
{"rules":[{"field":"CustomerID","op":"equal","value":"VINET"}, {"field":"CustomerID","op":"equal","value":"TOMSP"}],"op":"or"}
],
"op":"and"
}
翻譯結果:
Text:([OrderDate] < @p1 and ([CustomerID] = @p2 or [CustomerID] = @p3))
Parameters:
p1:2012-01-01
p2:VINET
p3:TOMSP
這個過濾規則分爲三個部分:【分組】、【規則】(字段、值、操做符)、【操做符】(and or),而自身就是一個分組。
這種簡單的結構就能夠知足所有的狀況。
固然,上面提到的這些條件都是在前臺定義(多是用戶在搜索框本身輸入的)的,而在後臺,咱們可能會定義一下【隱藏條件】,好比說 【員工只能查看本身的】,要怎麼作呢,其實很簡單,只須要在後臺接收到這個過濾條件(前臺toJSON,後臺解析JSON)之後,再加上一個過濾規則(隱藏條件):
{field:'EmployeeID',op:'equal',value:5}
能夠將原來的過濾規則當作一個分組加入進行:
{op:'and',groups:[
{"rules":[{"field":"OrderDate","op":"less","value":"2012-01-01"}],
"groups":[
{"rules":[{"field":"CustomerID","op":"equal","value":"VINET"},{"field":"CustomerID","op":"equal","value":"TOMSP"}],"op":"or"}
],"op":"and"}
],rules:[{field:'EmployeeID',op:'equal',value:5}]
}
翻譯以下:
Text:
([EmployeeID] = @p1 and ([OrderDate] < @p2 and ([CustomerID] = @p3 or [CustomerID] = @p4)))
Parameters:
p1:5
p2:2012-01-01
p3:VINET
p4:TOMSP
這樣的【條件規則】纔是咱們想要的,不只在前端能夠很好地解析,也能夠在後臺進行處理。在後臺咱們會定義跟這種數據結構對應的類,那麼再定義一個翻譯成SQL的類:
說了這些,能夠開始介紹如何實現【數據規則】了:
上面提到的【隱藏條件】,就是我介紹的【數據規則】
試想一些,這樣 前臺的過濾規則,再加上咱們之間定義好的 【數據權限】控制 過濾條件。不就達到目的了嗎。
先看看咱們在數據庫裏保存的這些【數據規則】:
看不明白?那來個清楚一點的:
規則描述
訂單:【訂單管理員和演示角色能夠查看全部的】,【訂單查看員】只能查看本身的
產品:【基礎信息錄入員和演示角色能夠查看全部的】,【供應商】只能查看本身的
{CurrentEmployeeID}表示當前的員工。
實質上,咱們還能夠根據當前用戶信息定義須要的參數,好比:
{CurrentUserID} 當前用戶Id ,對應表【CF_User】
{CurrentRoleID} 當前角色Id ,對應表 【CF_Role】
{CurrentDeptID} 當前用戶部門Id,對應表【CF_Department】
{CurrentEmployeeID} 當前用戶員工Id,對應表【Employees】(CF_User.EmployeeID)
{CurrentSupplierID} 當前用戶供應商Id,對應表【Suppliers】(CF_User.SupplierID)
在數據庫中咱們直接保存這些用戶參數,在「翻譯」規則成爲SQL時,會替換掉:
好比查看訂單,咱們獲得的SQL,多是這樣的:
Text: SELECT * FROM (SELECT TOP 20 * FROM (SELECT TOP 40 * FROM [Orders] WHERE ( 1=1 and ((@p1 in (@p2,@p3)) or (@p4 = @p5 and [EmployeeID] = @p6))) ORDER BY OrderID ASC) AS tmptableinner ORDER BY OrderID DESC) AS tmptableouter ORDER BY OrderID ASC
Parameters:
@p1[Int32] = 7
@p2[Int32] = 2
@p3[Int32] = 6
@p4[Int32] = 7
@p5[Int32] = 7
@p6[Int32] = 1
{CurrentRuleID} 替換爲 7
{CurrentEmployeeID} 替換爲1
下圖是咱們設計【數據權限】的界面,能夠選擇全部的字段,包括幾個用戶信息:
這些字段不只僅只是在文本框中輸入值,那麼能夠自定義數據來源:
var fieldEditors = {};
fieldEditors['Orders'] = {
'ShipCity': { type: 'combobox',
options: {
width: 200,
url: "../handler/select.ashx?view=Orders&idfield=ShipCity&textfield=ShipCity&distinct=true"
}
}
};
效果界面:
既然是數據權限控制,若是有一個統一的數據接收入口,咱們卻是能夠利用這個入口作一些工做。
好比【ligerRM權限管理系統】統一使用 grid.ashx 這個數據處理程序做爲列表數據的接收入口。
有了統一的接口,方便作權限的控制,使用過 ligerGrid Javascript表格,或者相似插件的朋友,應該比較清楚服務器的交互原理。
在grid.ashx中,咱們會經過
【視圖/表名 】、 【排序信息】、【分頁信息】、【過濾信息】
這幾個指標來獲取指定的數據。
而在實際的業務中,可能會引入權限的控制。好比某某【資源】,只能由當前用戶自身才能查看,或者只能由當前用戶部門及上級部門才能查看。對於這些控制,咱們採用對這些可能作權限控制的【資源】註冊一組【條件規則】的方式來進行。
咱們將找到view定義好的【數據權限規則】,而後和用戶在前臺搜索框輸入的【搜索規則】合併:
上面的代碼就是數據條件合併的例子,這樣便獲得了咱們最終須要的 過濾規則。
本文提出了數據權限的一種實現思路,只是本人在作一個web應用時構思的方案,談不上規範,歡迎提出你的建議意見。
能夠在http://case.ligerui.com體驗實際的應用效果。