web開發(fā)中的權(quán)限設(shè)計拙見一二(1)----設(shè)計思路
Posted on 2007-01-02 12:11 江上一葉舟 閱讀(7429) 評論(21) 編輯 收藏 所屬分類: 軟件藝術(shù)最近項目的項目很奇怪,一個大項目(系統(tǒng))里包含了很多小的子系統(tǒng),而這些子系統(tǒng)中都有權(quán)限控制的部分,這件事情挺讓我頭痛的,記得一年前在沈陽,我曾經(jīng)有一段時間也因因這個問題而疲于奔命,為什么說疲于奔命呢?由于當時項目進度不允許,導致最終系統(tǒng)權(quán)限模塊草草了事,每個模塊都是由讀權(quán)限字符串來控制用戶ACL,當用戶無法訪問時,提示權(quán)限不夠。這么做對用戶是很不負責任的,既然讓用戶看到了操作的方式和界面,為什么又告訴用戶沒有權(quán)限呢?我開始懷疑我們是否應(yīng)該在底層就封殺用戶的訪問權(quán)限。
現(xiàn)在項目開展起來了,雖然目前我已經(jīng)有了對權(quán)限控制的一套方案,并且實施成了我的可重用框架代碼,雖然目前的權(quán)限也是基于眾星捧月的AOP思想,但我至今對權(quán)限設(shè)計仍有兩個疑惑:
疑惑一:很多同行提出方案,想要在底層就截取用戶權(quán)限,控制用戶對方法或者類的訪問。這樣做的好處在于可以將系統(tǒng)功能與業(yè)務(wù)邏輯松散耦合,并且實現(xiàn)簡單,結(jié)構(gòu)清晰,三兩個advisor、filter,或者acegi就能搞定,但在web程序中體現(xiàn)出了他的劣勢,當我們將用戶的訪問拒絕在業(yè)務(wù)邏輯之外的時候,我們此時是否應(yīng)該拋出異常提示用戶?一旦提示用戶沒有相應(yīng)的權(quán)限,我認為對于用戶來說,這就不是一個perfect practice。由此得出,我們根本就不應(yīng)該讓用戶做此次操作,而控制用戶操作的源頭就是界面,也就是說,在界面上我們就應(yīng)該對用戶的權(quán)限元素(如添加按鈕、功能菜單等)進行控制。此時,一對矛盾出現(xiàn)了,要控制界面上形形色色的元素只有兩種辦法,一,將權(quán)限與你的界面結(jié)合起來設(shè)計,這將違背AOP的思想,也使得系統(tǒng)控制模塊的重用性大大下降,二,我們借鑒primeton的想法,將權(quán)限控制的理念抽取出來,單獨做成一套權(quán)限系統(tǒng),解決你所有的需要權(quán)限控制的系統(tǒng)需求,這樣也有令人頭痛的問題,你的所有想用它來控制權(quán)限的系統(tǒng),必須界面上統(tǒng)一風格。或許這樣的方式對商業(yè)web系統(tǒng)是合適的,畢竟需要你大刀闊斧個性化的地方不多,但我們卻很難保證在未來幾年內(nèi)商業(yè)web系統(tǒng)的風格不改變。再者,開發(fā)這么一個系統(tǒng)也不是一蹴而就的事,在這個問題上一直讓我困惑不已。
疑惑二:大多應(yīng)用的權(quán)限判定是基于權(quán)限字符串的,但存儲在數(shù)據(jù)庫中的權(quán)限字符串能夠判定的權(quán)限并不多,在我們這次項目中,我引用了基于二進制的8421權(quán)限判定法則,我深深的感覺到權(quán)限字符串的弱勢,這使我想起了中國古老一套數(shù)學理論-“盈不足術(shù)”,超遞增序列的魅力在我眼前滑過,
首先我來解釋一下盈余不足理論:有十只盒子,第一個盒子里放一個盤子,第二個盒子里放兩只,第三個盒子里放四只,第四個盒子里放八只……第九個盒子里放256只,第十個盒子放512只,即第N只箱子里放2^(N-1)只盤子,一共1023只。那么命題如下:在1023這個數(shù)字之內(nèi),任何一個數(shù)目都可以由這十只盒子里的幾只組合相加而成。那么1、2、4、8、16、32、64、128、256、512這個序列為什么有這么個魔力?這個數(shù)列的特點:1、每項是后一項的二倍,2、每項都比前面所有項的和大,而且大1。這個1就是關(guān)鍵,就因為這個1,它才可以按1遞增,拼出總和之內(nèi)任意一個整數(shù)。這個序列叫做超遞增序列,它是解決背包問題的基礎(chǔ)。3、拼出總和之內(nèi)任意一個整數(shù)可以由這個序列中的一些數(shù)構(gòu)成,且構(gòu)成方法唯一,據(jù)說是密碼學中的NP定理。譬如說這個數(shù)列總合中20這個數(shù),只能由16+4一種方法構(gòu)成,由此延伸出來,如果綜合中這個數(shù)據(jù)代表一個權(quán)值,我們可以解出它的所有構(gòu)成參數(shù)(操作),如20這個數(shù)據(jù),我們可以挨個和序列中每一項按位與,得出來如果不等于0,就說明他是由這個數(shù)構(gòu)成的。
保存權(quán)值到int還是varchar對于我們來說是個問題,當然,保存字符串的好處是運算壓力小。我們可能聽過一個故事,就是把這個超遞增序列延伸到第64項,就是那個術(shù)士和皇帝在國際象棋棋盤上要米粒的傳說。64項的和是一個天文數(shù)字!但計算機本身就是一個只認識二進制的機器!很多程序員整天只關(guān)心架構(gòu),甚至不知道或者不關(guān)心位操作是什么玩意,當然我們有朋友擔心數(shù)據(jù)庫的int不夠長,那么既然可以保存一個只有0、1組成的varchar字符串,為什么不能保存一個十六進制的字符串,有人規(guī)定varchar只能保存01嗎?十六進制串的長度正好是二進制的四分之一。
由此我們可以無限制的擴展權(quán)值操作。
在最近的項目里,我對權(quán)限的控制分成兩個部分,第一就是用戶體驗上,我設(shè)置了一個權(quán)限標簽,從數(shù)據(jù)庫中抽取權(quán)限信息,然后做到標簽里,也湊或算成是界面AOP了,第二就是底層的攔截,用了Spring 的AOP,為的是防止權(quán)限沖突,雙管齊下。暫時解決權(quán)限所需,另外在算法上我用了16進制的權(quán)限判別代碼,雖然配置較麻煩,寫完代碼還要寫文檔說明,不過也解決了權(quán)限繁雜又多的問題,暫時就這樣了,嘿嘿,以后有空再研究。