這里有必要先對(duì)權(quán)限控制方式本身做一下討論,有些系統(tǒng)用菜單進(jìn)行權(quán)限控制,比如"新建客戶","瀏覽客戶",往往用操作名加上一個(gè)資源名代表一個(gè)權(quán)限,這樣的權(quán)限控制有一個(gè)缺陷,就是一般漏掉了一個(gè)資源范圍,比如瀏覽什么樣的客戶,就沒有說明。當(dāng)然也可以加上,比如瀏覽個(gè)人客戶,瀏覽部門客戶,瀏覽公司客戶,但是隨著需要控制資源類型的增加,通過不斷增加菜單項(xiàng)來進(jìn)行控制,并不是一個(gè)很好的方法。在B/S系統(tǒng)中的菜單控制,有2重意義,一是真正的菜單,沒有權(quán)限的用戶就看不到相應(yīng)的菜單項(xiàng),另外一個(gè)為了防止用戶任意提交信息,在服務(wù)器端對(duì)用戶提交的URL進(jìn)行檢查,看是不是有這個(gè)權(quán)限。比如對(duì)于用戶請(qǐng)求的ListCus.do就要先檢查用戶是否有訪問這個(gè)URL的權(quán)限: public class ListCusAction extends Action{ ... public ActionForward execute(...){ ... //必須提供這樣的檢查的方法。 PermitUtil.checkPermit(userID,url); list = dao.query(sql); ... } } 另外一種控制辦法是對(duì)操作和操作資源定義權(quán)限,比如用戶A可以對(duì)客戶進(jìn)行修改操作。這樣細(xì)粒度的權(quán)限控制對(duì)程序的擴(kuò)展性和安全性有很大好處,但實(shí)現(xiàn)比前一種費(fèi)時(shí),下面通過分析說明2種方式的特點(diǎn)。
作為客戶系統(tǒng)最簡(jiǎn)單的一種考慮,是每個(gè)用戶治理自己的客戶。這種請(qǐng)況下,我們通過在Customer表中增加一個(gè)用戶字段,就可以知道這個(gè)客戶對(duì)應(yīng)于哪一個(gè)用戶。如下面的類所示: public class Customer{ PRivate String ctmNo; private String ctmName; ... private String userID; //getters and setters } 那么通過菜單的控制方式,我們認(rèn)為每個(gè)有客戶創(chuàng)建權(quán)的用戶都可以創(chuàng)建屬于自己的客戶,什么叫客戶創(chuàng)建權(quán),就是可以看到和選擇“添加客戶”菜單項(xiàng)的用戶。同時(shí),“客戶瀏覽”就表示瀏覽屬于自己的客戶,這樣一來,創(chuàng)建客戶的用戶可以看到自己的客戶,并進(jìn)行操作,我們通過菜單控制權(quán)限方式完成了這個(gè)需求。需要注重的是:在用戶進(jìn)行相應(yīng)操作前,我們先判定這個(gè)客戶是不是屬于這個(gè)用戶,是就可以操作,不是就不可以。如下例所示: public class DelCusAction extends Action{ ... public ActionForward execute(...){ ... //必須提供這樣的檢查的方法。 dao.checkPermit(customer,userID); dao.delete(customer); ... } } 進(jìn)行這個(gè)判定是出于安全性考慮的,非凡在B/S架構(gòu)的軟件系統(tǒng)中,你無法阻止客戶向服務(wù)器端任意提交信息。比如用戶A不使用菜單,向服務(wù)器請(qǐng)求"DelCus.do?ctmNo=No1",而No1屬于用戶B,假如不進(jìn)行判定,客戶就被刪除掉了。所以光靠控制菜單項(xiàng)的顯示完成權(quán)限控制,并不完整。
現(xiàn)在我們接觸一個(gè)更復(fù)雜一點(diǎn)的需求,就是客戶轉(zhuǎn)移,把客戶從一個(gè)用戶,指派給另外一個(gè)用戶。在上面的實(shí)現(xiàn)基礎(chǔ)上,只要添加一個(gè)接口,改變客戶所屬的用戶,問題也就解決了。如下: public class ChangeUserAction extends Action{ ... public ActionForward execute(...){ ... dao.checkPermit(customer,userID); //改變客戶所屬用戶到newUserID。 dao.changeUser(customer,newUserID); ... } }
每個(gè)用戶現(xiàn)在可以治理自己的客戶,也可以把客戶交給別人治理,問題解決得完美。我們?cè)倏紤]實(shí)際的情況,一個(gè)客戶可能可以被一個(gè)用戶看,但是不可以被修改或者刪除。我們?cè)趺纯刂七@樣的權(quán)限,加一個(gè)菜單項(xiàng)-"察看客戶",用戶A可以執(zhí)行這個(gè)操作,但是不可以選擇菜單項(xiàng)-"修改客戶",針對(duì)每一個(gè)動(dòng)作,我們加了一個(gè)菜單項(xiàng)。我們需要控制的內(nèi)容變得越來越多。再深入分析,可以發(fā)現(xiàn),其實(shí)上面的方案已經(jīng)不能滿足需求。 2個(gè)客戶我們?nèi)绾沃酪粋€(gè)可以被用戶A看,一個(gè)不可以,出現(xiàn)了一個(gè)資源的治理問題,必須有另外的方案來進(jìn)行這種權(quán)限的控制。我們?cè)O(shè)計(jì)一個(gè)資源類: public class CustomerResource{ //客戶編號(hào) private String ctmNo; //操作類型 private String action; //用戶 private String userID; //setters and getters. } 再設(shè)計(jì)一個(gè)資源治理類: public class CustomerResourceManager{ private static List resources; private static void addResources(CustomerResource resource){ resources.add(resource); } //從數(shù)據(jù)庫裝載相應(yīng)用戶的客戶操作權(quán)限。 private static void retrieveResources(String userID){ ... } //檢查相應(yīng)的權(quán)限是否在resources中存在。 private static boolean checkPermit(CustomerResource permit){ ... } } 客戶端使用類: public class DelCusAction extends Action{ ... public ActionForward execute(...){ ... //必須提供這樣的檢查的方法。 CustomerResource resource = new CustomerResource(); resource.setCtmNo(customer.getCtmNo()); resource.setAction("DELETE"); resource.setUserID(userID); if(CustomerResourceManager.checkPermit(resource)){ dao.delete(customer); } ... } } 在采用了上面的權(quán)限檢查方式后,我們可以對(duì)每一個(gè)客戶指定相應(yīng)操作人的權(quán)限,對(duì)客戶的操作就限制在了操作類型的多少上,基本的有“UPDATE,INSERT,DELETE,LIST,VIEW”等,其余也可以定義“SUBMIT”等操作,具體取決于系統(tǒng)需求。這樣的好處是權(quán)限控制非常靈活,客戶可以作為資源在用戶之間自由流動(dòng)。但是作為一個(gè)給用戶最終使用的權(quán)限系統(tǒng),這樣的卻體系不適合于出現(xiàn)在用戶權(quán)限系統(tǒng)設(shè)置里面,不能叫每個(gè)用戶都去自己指定自己創(chuàng)建客戶的操作范圍,最好是通過這種方式實(shí)現(xiàn)默認(rèn)的業(yè)務(wù)和權(quán)限設(shè)定,滿足了用戶需求,又屏蔽復(fù)雜性。在完整的系統(tǒng)中,基于菜單的控制也是必不可少,這樣的方式方便用戶使用,也更易于用戶理解。 2者通常應(yīng)結(jié)合使用。