本文將對這種設(shè)計思想作進一步的擴展,介紹數(shù)據(jù)權(quán)限的設(shè)計方案训枢。
權(quán)限控制可以理解托修,分為這幾種 :
【功能權(quán)限】:能做什么的問題,如增加產(chǎn)品恒界。
【數(shù)據(jù)權(quán)限】:能看到哪些數(shù)據(jù)的問題睦刃,如查看本人的所有訂單。
【字段權(quán)限】:能看到哪些信息的問題十酣,如供應(yīng)商賬戶涩拙,看不到角色、 部門等信息耸采。
上面提到的那種設(shè)計就是【功能權(quán)限】兴泥,這種設(shè)計有一定的局限性,對于主體虾宇,只能明確地指定搓彻。對于不明確的,在這里可能就沒辦法處理嘱朽。比如下面這幾種情況:
數(shù)據(jù)僅當(dāng)前部門及上級可見
數(shù)據(jù)僅當(dāng)前用戶(本人)可見
類似這樣的就需要用到上面提的數(shù)據(jù)權(quán)限旭贬。
初步分析
【數(shù)據(jù)權(quán)限】是在【功能權(quán)限】的基礎(chǔ)上面進一步的擴展,比如可以查看訂單屬于【功能權(quán)限】的范圍搪泳,但是可以查看哪些訂單就是【數(shù)據(jù)權(quán)限】的工作了稀轨。
在設(shè)計中,我們規(guī)定好如果沒有設(shè)置了數(shù)據(jù)權(quán)限規(guī)則岸军,那么視為允許查看全部的數(shù)據(jù)奋刽。
幾個概念
【資源】:數(shù)據(jù)權(quán)限的控制對象,業(yè)務(wù)系統(tǒng)中的各種資源凛膏。比如訂單單據(jù)杨名、銷售單等。屬于提到的【領(lǐng)域】中的一種
【主體】:用戶猖毫、部門台谍、角色等。
【條件規(guī)則】:用于檢索數(shù)據(jù)的條件定義
【數(shù)據(jù)規(guī)則】:用于【數(shù)據(jù)權(quán)限】的條件規(guī)則
應(yīng)用場景
**1吁断,訂單趁蕊,可以由本人查看 **
**2,銷售單仔役,可以由本人或上級領(lǐng)導(dǎo)查看 **
3掷伙,銷售單,銷售人員可以查看自己的又兵,銷售經(jīng)理只查看 銷售金額大于100,000的任柜。
我們能想到直接的方法卒废,在訪問數(shù)據(jù)的入口加入SQL Where條件來實現(xiàn),組織sql語句:
1宙地,whereUserID={CurrentUserID}
2摔认,whereUserID={CurrentUserID}or{CurrentUserID}in(領(lǐng)導(dǎo))
3,whereUserID={CurrentUserID}or({CurrentUserID} in(銷售經(jīng)理)and銷售金額>100000)
這些一個一個的"條件"宅粥,本文簡單理解為一個【數(shù)據(jù)規(guī)則】参袱,通常會與原來我們前臺的業(yè)務(wù)過濾條件合并再檢索出數(shù)據(jù)。
這是一種最直接的實現(xiàn)方式秽梅,在【資源】上面加一個【數(shù)據(jù)規(guī)則】(比如上面的三點)抹蚀。
這樣設(shè)計就是
【資源】 - 【數(shù)據(jù)規(guī)則】
我又覺得不同的人應(yīng)該對應(yīng)不同的規(guī)則,那么也可以理解為企垦,一個用戶對應(yīng)不同的角色环壤,每一個角色有不一樣的【數(shù)據(jù)規(guī)則】,那么設(shè)計就變成【資源】 - 【主體】 - 【數(shù)據(jù)規(guī)則】竹观。
根據(jù)提供者的不同镐捧,準(zhǔn)備不同的權(quán)限應(yīng)對策略。
這里可以簡單地介紹一下臭增,這個方案至少需要2張表懂酱,一個是 **【資源,主體誊抛,規(guī)則關(guān)系表】**列牺、一個是【數(shù)據(jù)規(guī)則表】
關(guān)系表不能直接保存角色,因為你不確定什么時候業(yè)務(wù)需要按照【部門】或者【分公司】來定義數(shù)據(jù)規(guī)則
** 于是可以用Master拗窃、MasterKey 類似這樣的兩個字段來確定一個【主體】**
用XML方式的話是這樣配置的(放在數(shù)據(jù)庫也類似):
<?xml version="1.0" encoding="utf-8"?>
<settings>
** <rule view="訂單" role="銷售人員">**
** 銷售員 = {CurrentUserID}**
** </rule>**
** <rule view="訂單" role="總銷售經(jīng)理">**
** 銷售金額 > 100000**
** </rule>**
** <rule view="訂單" role="區(qū)域銷售經(jīng)理">**
** 銷售金額 > 100000 and 區(qū)域 = {當(dāng)前用戶所屬區(qū)域}**
** </rule>**
</settings>
對于這種方式有興趣的朋友也可以試一下瞎领,兩種方式的【數(shù)據(jù)規(guī)則】是一樣的,但是本文沒有采用第二種設(shè)計方式随夸,因為它多了一層處理邏輯九默,我以為應(yīng)該設(shè)計越簡單越好,就采用第一種方式:
【資源】 - 【數(shù)據(jù)規(guī)則】
當(dāng)然宾毒,上面是用SQL的方式來確定條件規(guī)則的驼修,我們當(dāng)然不會這么做。SQL雖然靈活诈铛,但是我們很難去維護乙各,也不知道SQL在我們的界面UI上面如何體現(xiàn)。難不成直接用一個文本框來顯示幢竹。這樣對應(yīng)一個開發(fā)人員來說不是問題耳峦,可是對應(yīng)系統(tǒng)管理員,很容易出問題焕毫。所以我們需要有另一方式來確定這一規(guī)則蹲坷,并最終可以轉(zhuǎn)換成我們的SQL語句驶乾。
我們的設(shè)計關(guān)鍵在于如何規(guī)范好這些【數(shù)據(jù)規(guī)則】,這個規(guī)則必須是對前端友好的冠句,而且是對后臺友好的轻掩,JSON顯然是很好的方式。
規(guī)則說明:
1懦底,數(shù)據(jù)權(quán)限規(guī)則總是:{屬性 條件 允許值}
2,數(shù)據(jù)權(quán)限規(guī)則可以合并罕扎。比如 ( {當(dāng)前用戶 屬于 銷售人員} and {訂單銷售員 等于 當(dāng)前用戶} ) Or {當(dāng)前用戶 屬于 銷售經(jīng)理}
3聚唐,最終我們會用JSON格式
在檢索數(shù)據(jù)時會先判斷有沒有注冊了某某【資源】的【條件規(guī)則】,如果有腔召,那么加載這個【條件規(guī)則】并合并到我們前臺的【搜索條件】(你的業(yè)務(wù)界面應(yīng)該有一個搜索框吧)
如下圖定義了客戶信息的搜索框杆查,我們搜索客戶ID包括AN,我們組織成的規(guī)則將會是:
{"rules":[{"field":"CustomerID","op":"like","value":"AN","type":"string"}],"op":"and"}
為了更好地理解【數(shù)據(jù)規(guī)則】臀蛛,這里介紹一下【通用查詢機制】
權(quán)限控制總離不開一些條件的限制(比如查看當(dāng)前部門的單據(jù))亲桦,如果沒有完善的通用查詢規(guī)則機制,那么在做權(quán)限條件過濾的時候你會覺得很別扭浊仆。這里介紹一個通用查詢方案客峭,然后再介紹如何實現(xiàn)【數(shù)據(jù)規(guī)則】。
早些時候我寫過一篇關(guān)于ligerGrid結(jié)合.net設(shè)計通用處理類的文章《 jQuery liger ui ligerGrid 打造通用的分頁排序查詢表格(提供下載) 》抡柿。里面提到的過濾信息是直接的SQL語句舔琅。這是不可靠,而且不安全的洲劣。
在前端傳輸給后臺的過濾信息不應(yīng)該是直接的SQL备蚓,而應(yīng)該是一組過濾規(guī)則。在ligerui V1.1.8 已經(jīng)加入了一個條件過濾器插件囱稽,這個插件組成的規(guī)則數(shù)據(jù)才是我受推薦的:
比如如下
{"rules":[{"field":"OrderDate","op":"less","value":"2012-01-01"},{"field":"CustomerID","op":"equal","value":"VINET"}],"op":"and"}
規(guī)則描述:
查找顧客VINET所有訂單時間小于2011-01-01的單據(jù)
這樣的數(shù)據(jù)是安全的郊尝,而且是通用的(你甚至可以再加一個OR子查詢)。無論是在前端還是后臺战惊,無論你使用什么樣的組件流昏,都可以很好地利用。
通用后臺的翻譯样傍,就可以生成這樣SQL的參數(shù):
Text:
( [OrderDate]< @p1 and [CustomerID]=@p2)
Parameters:
p1:2012-01-01
p2:VINET
下面來點復(fù)雜的:查找 顧客VINET或者TOMSP横缔,所有訂單時間小于2011-01-01的單據(jù)
{
"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]<@p1and([CustomerID]=@p2or[CustomerID]=@p3))
Parameters:
p1:2012-01-01
p2:VINET
p3:TOMSP
這個過濾規(guī)則分為三個部分:【分組】、【規(guī)則】(字段衫哥、值茎刚、操作符)、【操作符】(and or)撤逢,而自身就是一個分組膛锭。
這種簡單的結(jié)構(gòu)就可以滿足全部的情況粮坞。
當(dāng)然,上面提到的這些條件都是在前臺定義(可能是用戶在搜索框自己輸入的)的初狰,而在后臺莫杈,我們可能會定義一下【隱藏條件】,比如說 【員工只能查看自己的】奢入,要怎么做呢筝闹,其實很簡單,只需要在后臺接收到這個過濾條件(前臺toJSON,后臺解析JSON)以后,再加上一個過濾規(guī)則(隱藏條件):
{field:'EmployeeID',op:'equal',value:5}
可以將原來的過濾規(guī)則當(dāng)做一個分組加入進行:
{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
這樣的【條件規(guī)則】才是我們想要的洽腺,不僅在前端可以很好地解析,也可以在后臺進行處理议双。在后臺我們會定義跟這種數(shù)據(jù)結(jié)構(gòu)對應(yīng)的類,那么再定義一個翻譯成SQL的類:
數(shù)據(jù)權(quán)限規(guī)則
說了這些捉片,可以開始介紹如何實現(xiàn)【數(shù)據(jù)規(guī)則】了:
上面提到的【隱藏條件】平痰,就是我介紹的【數(shù)據(jù)規(guī)則】
試想一些,這樣 前臺的過濾規(guī)則伍纫,再加上我們之間定義好的 【數(shù)據(jù)權(quán)限】控制 過濾條件宗雇。不就達(dá)到目的了嗎。
先看看我們在數(shù)據(jù)庫里保存的這些【數(shù)據(jù)規(guī)則】:
看不明白翻斟?那來個清楚一點的:
規(guī)則描述
訂單:【訂單管理員和演示角色可以查看所有的】逾礁,【訂單查看員】只能查看自己的
產(chǎn)品:【基礎(chǔ)信息錄入員和演示角色可以查看所有的】,【供應(yīng)商】只能查看自己的
{CurrentEmployeeID}表示當(dāng)前的員工访惜。
實質(zhì)上嘹履,我們還可以根據(jù)當(dāng)前用戶信息定義需要的參數(shù),比如:
{CurrentUserID} 當(dāng)前用戶Id 债热,對應(yīng)表【CF_User】
{CurrentRoleID} 當(dāng)前角色I(xiàn)d 砾嫉,對應(yīng)表 【CF_Role】
{CurrentDeptID} 當(dāng)前用戶部門Id,對應(yīng)表【CF_Department】
{CurrentEmployeeID} 當(dāng)前用戶員工Id窒篱,對應(yīng)表【Employees】(CF_User.EmployeeID)
{CurrentSupplierID} 當(dāng)前用戶供應(yīng)商Id焕刮,對應(yīng)表【Suppliers】(CF_User.SupplierID)
在數(shù)據(jù)庫中我們直接保存這些用戶參數(shù),在“翻譯”規(guī)則成為SQL時墙杯,會替換掉:
比如查看訂單配并,我們得到的SQL,可能是這樣的:
Text:
SELECTFROM(SELECTTOP20FROM(SELECTTOP40*FROM[Orders]WHERE(1=1and((@p1in(@p2,@p3))or(@p4=@p5and[EmployeeID]=@p6)))ORDERBYOrderIDASC)AStmptableinnerORDERBYOrderIDDESC)AStmptableouterORDERBYOrderIDASC
Parameters:
@p1[Int32]=7
@p2[Int32]=2
@p3[Int32]=6
@p4[Int32]=7
@p5[Int32]=7
@p6[Int32]=1
{CurrentRuleID} 替換為 7
{CurrentEmployeeID} 替換為1
下圖是我們設(shè)計【數(shù)據(jù)權(quán)限】的界面高镐,可以選擇所有的字段溉旋,包括幾個用戶信息:
這些字段不僅僅只是在文本框中輸入值,那么可以自定義數(shù)據(jù)來源:
varfieldEditors={};
fieldEditors['Orders']={
'ShipCity': { type:'combobox',
options: {
width:200,
url: "../handler/select.ashx?view=Orders&idfield=ShipCity&textfield=ShipCity&distinct=true"
}
}
};
效果界面:
既然是數(shù)據(jù)權(quán)限控制嫉髓,如果有一個統(tǒng)一的數(shù)據(jù)接收入口观腊,我們倒是可以利用這個入口做一些工作邑闲。
比如【ligerRM權(quán)限管理系統(tǒng)】統(tǒng)一使用 grid.ashx 這個數(shù)據(jù)處理程序作為列表數(shù)據(jù)的接收入口。
有了統(tǒng)一的接口梧油,方便做權(quán)限的控制苫耸,使用過 ligerGrid Javascript表格,或者類似插件的朋友儡陨,應(yīng)該比較清楚服務(wù)器的交互原理褪子。
在grid.ashx中,我們會通過
【視圖/表名 】迄委、 【排序信息】褐筛、【分頁信息】、【過濾信息】
這幾個指標(biāo)來獲取指定的數(shù)據(jù)叙身。
而在實際的業(yè)務(wù)中,可能會引入權(quán)限的控制硫狞。比如某某【資源】信轿,只能由當(dāng)前用戶自身才能查看,或者只能由當(dāng)前用戶部門及上級部門才能查看残吩。對于這些控制财忽,我們采用對這些可能做權(quán)限控制的【資源】注冊一組【條件規(guī)則】的方式來進行。
我們將找到view定義好的【數(shù)據(jù)權(quán)限規(guī)則】泣侮,然后和用戶在前臺搜索框輸入的【搜索規(guī)則】合并:
上面的代碼就是數(shù)據(jù)條件合并的例子即彪,這樣便得到了我們最終需要的 過濾規(guī)則。
結(jié)語
本文提出了數(shù)據(jù)權(quán)限的一種實現(xiàn)思路活尊,只是本人在做一個web應(yīng)用時構(gòu)思的方案隶校,談不上規(guī)范,歡迎提出你的建議意見蛹锰。
可以在http://case.ligerui.com體驗實際的應(yīng)用效果深胳。