J2EE應(yīng)用程序的授權(quán)概念和解決方案
什么是授權(quán)?
授權(quán)就是基于用戶的權(quán)限或用戶的類別(在這種情況下,“用戶”可能指某個(gè)外部系統(tǒng),而不一定是個(gè)人)管理對(duì)受保護(hù)系統(tǒng)資源的訪問。因此,授權(quán)假定已進(jìn)行了身份驗(yàn)證,因?yàn)槿绻恢烙脩羰钦l,則根本無法提供恰當(dāng)?shù)脑L問控制——而不是在通常情況下所有用戶都具有相同的權(quán)限。稍后我們將詳細(xì)說明各種授權(quán)模式或方式,但首先我們將對(duì)其做一個(gè)簡(jiǎn)要的總結(jié)。
基于角色的授權(quán)
基于角色的授權(quán)基于用戶是某類用戶的成員這一事實(shí)提供對(duì)資源的訪問。這通常依賴于用戶具有在用戶注冊(cè)中心定義的組標(biāo)識(shí)。此方法定義受保護(hù)系統(tǒng)的各個(gè)組件,并將這些組件映射到能夠?qū)ζ溥M(jìn)行訪問的用戶組。這種類型的授權(quán)是 J2EE 領(lǐng)域的標(biāo)準(zhǔn)模式??偟恼f來,這是一種非常粗粒度的安全形式,用于處理對(duì)系統(tǒng)的功能領(lǐng)域的訪問,可以由 URL 或?qū)?EJB 組件的方法調(diào)用進(jìn)行定義。這并不是說無法使用角色定義更細(xì)粒度的保護(hù),但這樣做的效果通常不好。
基于實(shí)例的授權(quán)
與 J2EE 基于角色的授權(quán)通常提供的授權(quán)相比,此模式提供了更細(xì)粒度的授權(quán),可以一直細(xì)化到項(xiàng)目?jī)?nèi)的單個(gè)對(duì)象的級(jí)別。我們現(xiàn)在討論的是數(shù)據(jù)保護(hù),而不只是功能保護(hù)。
基于關(guān)系/所有權(quán)的授權(quán)
這種類型的授權(quán)是基于實(shí)例的模式的一個(gè)特例,在基于實(shí)例的模式中,在應(yīng)用程序的數(shù)據(jù)結(jié)構(gòu)內(nèi)存在用戶與某些其他受保護(hù)的數(shù)據(jù)之間的所有權(quán)關(guān)系。這通常意味著必須將授權(quán)規(guī)則嵌入到應(yīng)用程序本身的數(shù)據(jù)訪問邏輯之中。
用戶界面自定義
雖然這不是真正的授權(quán)類型,但與此緊密相關(guān)的一個(gè)問題是對(duì)用戶界面進(jìn)行自定義,以使其僅顯示允許特定用戶看到的特定功能。例如,菜單或門戶頁將不會(huì)顯示未授權(quán)用戶訪問的鏈接或 Portlet。這實(shí)際上是一種預(yù)先授權(quán);授權(quán)詢問“我能這樣做嗎?”,而預(yù)先授權(quán)則詢問一個(gè)略為不同的問題“如果我詢問是否能進(jìn)行此操作,答案會(huì)是什么?”不過,正如我們稍后將看到的,這兩種情況下的編碼技術(shù)可以完全相同。
J2EE 基于角色的授權(quán)
基于角色的授權(quán)通過僅將資源提供給已分配相應(yīng)角色的用戶來保護(hù)資源。J2EE 提供了通過使用聲明和編程技術(shù)來完成此任務(wù)的標(biāo)準(zhǔn)方法。我們稍后將在授權(quán)技術(shù)部分中詳細(xì)討論 J2EE 安全性。不過,務(wù)必理解 J2EE 領(lǐng)域中的角色概念。關(guān)鍵要記住,J2EE 角色不是一組人員。它們是特定于應(yīng)用程序的邏輯名稱,可以在部署時(shí)映射到用戶和組。角色本身通常表示 J2EE 應(yīng)用程序中的權(quán)限集合。
實(shí)際定義哪些用戶屬于哪些組是在用戶注冊(cè)中心中進(jìn)行管理的。這可以是企業(yè) LDAP 服務(wù)器,也可以是僅由單個(gè)應(yīng)用程序使用的數(shù)據(jù)庫(kù),但用戶和組信息的存儲(chǔ)庫(kù)位于 J2EE 環(huán)境之外。在 J2EE 應(yīng)用程序內(nèi),您可以基于這些角色定義和命名安全角色及約束資源訪問。因此,角色是權(quán)限集合;為了使用它,必須在部署應(yīng)用程序時(shí)將其綁定到某一組用戶。可以按名稱將其映射到一個(gè)或多個(gè)用戶,也可以映射到一個(gè)或多個(gè)用戶組,還可以映射到二者的任意組合。術(shù)語主體通常是指用戶或用戶組。
您將看到,雖然 J2EE 角色可能通過簡(jiǎn)單的一對(duì)一映射緊密地映射到注冊(cè)中心組,但并不一定非得如此;在最簡(jiǎn)單的情況下,可以將“manager”角色映射到稱為“manager”的組,但 J2EE 角色的強(qiáng)大之處在于,它提供了出色的靈活性,能夠在組織發(fā)生更改時(shí)更改綁定,而無需進(jìn)行編程更改。以下列情況為例,假定特定的應(yīng)用程序功能僅對(duì)法律部門的員工可用,而這些員工都屬于 Department 102。可以在應(yīng)用程序中創(chuàng)建一個(gè)名為“l(fā)egal”的 J2EE 角色,并綁定到注冊(cè)中心組“Dept102”。如果以后對(duì)該部門進(jìn)行了重組,其中一半的員工轉(zhuǎn)到了 Department 507,則可以更改綁定,以映射到“Dept102”和“Dept507”。然后,當(dāng)屬于這兩個(gè)部門的任何員工經(jīng)過了系統(tǒng)的身份驗(yàn)證后,就會(huì)向其授予“l(fā)egal”角色,系統(tǒng)會(huì)向其提供恰當(dāng)?shù)脑L問權(quán)限。
自定義基于角色的授權(quán)
即使使用編程 API,仍然會(huì)有因 J2EE 角色模型不夠靈活而難以滿足業(yè)務(wù)需求的情況。不過,在草率行事前,應(yīng)該對(duì)以 J2EE 模型為基礎(chǔ)(而不是將其替換)進(jìn)行構(gòu)建的可能性進(jìn)行分析。這樣做的優(yōu)勢(shì)包括:
利用本機(jī)應(yīng)用服務(wù)器身份驗(yàn)證對(duì)創(chuàng)建完全安全的系統(tǒng)至關(guān)重要。(請(qǐng)參見 IBM WebSphere 開發(fā)者技術(shù)期刊: WebSphere Application Server V6 高級(jí)安全性加強(qiáng)——第 1 部分)。
良好的安全性可提供深度防御;使用多種方法并不一定是壞事。
可以首先使用 J2EE 執(zhí)行基本的聲明性安全功能,然后使用自定義方法來執(zhí)行更為詳細(xì)的身份驗(yàn)證邏輯。
從頭編寫可以免費(fèi)獲得的功能并不劃算!
另外要注意,有時(shí)候,可以通過應(yīng)用服務(wù)器安全運(yùn)行時(shí)的其他可插入功能來獲得所需的靈活性。例如,如果需要角色-組映射在運(yùn)行時(shí)具有動(dòng)態(tài)性(根據(jù)用戶登錄時(shí)提供的信息),則可以使用 JAAS 登錄擴(kuò)展來滿足此需求(請(qǐng)參見 Advanced authentication in WebSphere Application Server)。在 WebSphere Application Server V5.1.1 或更高版本中,可以在 Trust Association Interceptor 或 JAAS 登錄模塊中創(chuàng)建動(dòng)態(tài)組成員身份。不過,如果角色與上下文相關(guān)(基于具體的應(yīng)用程序使用情況),則可能需要購(gòu)買或構(gòu)建授權(quán)框架。醫(yī)療應(yīng)用程序就是上下文使用模式的一個(gè)例子:醫(yī)生可以一次性登錄到系統(tǒng),但根據(jù)所查看的病人或醫(yī)療實(shí)體(如醫(yī)院或診所)的上下文的不同,該醫(yī)生的角色可能會(huì)從檢查醫(yī)師更改為主治醫(yī)師。此更改是在會(huì)話過程中動(dòng)態(tài)更改的,而不僅限于進(jìn)行身份驗(yàn)證時(shí)可用的信息。
只要認(rèn)為 J2EE 應(yīng)用程序授權(quán)本身不足以解決您的問題,下一個(gè)問題就是要確定是否構(gòu)建或購(gòu)買授權(quán)解決方案。IBM Tivoli® Access Manager 等企業(yè)安全產(chǎn)品提供了靈活的、基于策略的功能授權(quán)。不過,將這些功能與應(yīng)用服務(wù)器相集成可能有一定的挑戰(zhàn)性。您需要考慮的一些問題包括:
是否使用專用 API?
將提供何種功能?
授權(quán)請(qǐng)求外部化會(huì)帶來何種性能損失?
Java Authorization Contract for Containers (JACC) 是一種基于標(biāo)準(zhǔn)的方法,用于將外部安全管理器與應(yīng)用服務(wù)器集成。JACC 提供了將安全授權(quán)的權(quán)限檢查委托給外部提供程序的功能。由于授權(quán)檢查是在容器將控制權(quán)交給應(yīng)用程序前進(jìn)行的,因此 JACC 具有能夠清楚區(qū)分自定義授權(quán)邏輯和應(yīng)用程序邏輯的優(yōu)勢(shì),從而滿足了關(guān)注點(diǎn)分離的需求。不過,使用 JACC 時(shí)有一些方面需要特別注意,因此有必要更深入地探討 JACC,以準(zhǔn)確了解其執(zhí)行的工作以及可對(duì)其加以應(yīng)用的場(chǎng)合。我們稍后將對(duì) JACC 進(jìn)行更為詳細(xì)的討論。
基于實(shí)例的授權(quán)
顧名思義,基于實(shí)例的授權(quán)就是將訪問權(quán)限授予某個(gè)對(duì)象的特定實(shí)例?;趯?shí)例的授權(quán)通常使用訪問控制列表(Access Control List,ACL)來保護(hù)實(shí)例,而 ACL 存儲(chǔ)在某種類型的策略存儲(chǔ)區(qū)中,并且可用來制定訪問決策。可以將 J2EE 角色作為 ACL 使用,不過這個(gè)方法可能會(huì)不方便。正如我們前面所討論的,J2EE 角色終究只是一個(gè)名稱,是可以綁定到任何一組主體的邏輯構(gòu)造。這種方法不能很好地?cái)U(kuò)展來大量使用 ACL,也不能處理在應(yīng)用程序執(zhí)行時(shí)動(dòng)態(tài)修改 ACL 的情況;因?yàn)槲覀冊(cè)岬剑渴鹈枋龇趹?yīng)用程序啟動(dòng)時(shí)以靜態(tài)方式定義 J2EE 角色??紤]到這些限制,在許多情況下,更好的方法可能是使用外部安全解決方案或 ACL 基于實(shí)例的安全性自定義框架。
為了更便于理解,讓我們看一個(gè)例子。假定我們有一個(gè)新的 J2EE 人力資源系統(tǒng),可允許用戶執(zhí)行打印相關(guān)的任務(wù),如將文檔發(fā)送到打印機(jī)和查看與管理打印隊(duì)列。由于可打印數(shù)據(jù)的敏感特征,需要進(jìn)行某些限制。例如,可能存在有關(guān)授權(quán)哪些用戶使用或管理哪些打印機(jī)的規(guī)則。還可能存在有關(guān)在每天的特定時(shí)段使用打印機(jī)的規(guī)則;或許會(huì)考慮連夜在特定的打印機(jī)上打印敏感材料。
我們可以使用標(biāo)準(zhǔn)的 J2EE 基于角色的安全性來管理打印機(jī)訪問。例如,可以為打印機(jī)和所需訪問類型的每種組合定義一個(gè)角色,最終得到“Allowed_to_print_to_PrinterX”或“Allowed_to_manage_queue_for_PrinterY”之類的角色。然后,我們必須在應(yīng)用程序中編寫代碼,以使用 isUserInRole() 調(diào)用遍歷所有可能的角色,從而驗(yàn)證當(dāng)前用戶是否被授權(quán)執(zhí)行他們所嘗試的任何打印相關(guān)操作,而且我們必須將每個(gè)角色綁定到相應(yīng)的用戶注冊(cè)中心條目。
正如您所看到的,這并不是授權(quán)需求的一個(gè)條理非常清楚的實(shí)現(xiàn)。添加任何新打印機(jī)都會(huì)要求對(duì)應(yīng)用程序進(jìn)行更改;而有關(guān)何人可以進(jìn)行何種操作的規(guī)則的變化則會(huì)要求重新將這些角色綁定到主體,并重新部署應(yīng)用程序。此外,我們尚未開始考慮時(shí)段問題。我們需要能夠存儲(chǔ)與每臺(tái)打印機(jī)對(duì)應(yīng)的可配置信息,但在標(biāo)準(zhǔn) J2EE 授權(quán)模式中,實(shí)際上卻沒有任何地方能夠放置此類信息。
現(xiàn)在,如果我們要使用獨(dú)立的授權(quán)服務(wù),這可以極大地簡(jiǎn)化解決方案,因?yàn)槲覀儗⒅恍枰儐柺跈?quán)服務(wù),當(dāng)前用戶是否可以對(duì)某個(gè)對(duì)象執(zhí)行某項(xiàng)操作。所有這些都將外部化,從而極大地簡(jiǎn)化了編程模型,并使得對(duì)外部提供程序的更改以實(shí)時(shí)的方式反映到正在運(yùn)行的應(yīng)用程序中。我們稍后將再次對(duì)此問題進(jìn)行討論。
所有權(quán)關(guān)系
直接所有權(quán)是用戶和某些受保護(hù)數(shù)據(jù)間的一種十分常見的基于實(shí)例的關(guān)系。例如,在經(jīng)紀(jì)業(yè)應(yīng)用程序中,理財(cái)顧問可以查看其個(gè)人客戶的帳戶,但卻不能查看公司的其他客戶的帳戶。部門經(jīng)理可以查看所屬的理財(cái)顧問的所有客戶的所有帳戶,但卻不能查看其他部門的客戶的帳戶,諸如此類。在這種情況下,權(quán)限內(nèi)置到應(yīng)用程序的數(shù)據(jù)結(jié)構(gòu)中。
應(yīng)用程序很少會(huì)采用以下這種方式,即向用戶界面提供用于檢索所有數(shù)據(jù)的帳戶列表,然后再向每一行應(yīng)用基于 ACL 的權(quán)限。這里存在的主要問題是性能:與在 Java 代碼中進(jìn)行篩選相比,在數(shù)據(jù)庫(kù)引擎中進(jìn)行行篩選通常要快得多。盡管緩存可能在一定程度上減輕這種性能影響,但如果使用特定大小的數(shù)據(jù)塊檢索數(shù)據(jù)來支持用戶界面分頁,則可能出現(xiàn)另一個(gè)問題。如果篩選在應(yīng)用程序中進(jìn)行,則無法知道請(qǐng)求了多少行,因此可能導(dǎo)致通過多次調(diào)用來獲取單頁數(shù)據(jù)的情況。
遺憾的是,盡管存在種種缺陷,這種應(yīng)用程序端篩選方法卻被經(jīng)常使用。如果應(yīng)用程序使用對(duì)象關(guān)系映射工具來執(zhí)行數(shù)據(jù)訪問,然后嘗試以某種通用方式將安全機(jī)制應(yīng)用到實(shí)例化的類,則尤其可能出現(xiàn)問題。這是對(duì)計(jì)算資源的一種極為低效的使用,獲得可接受的性能和可伸縮性的可能性非常小。
唯一合理的方法是讓數(shù)據(jù)庫(kù)進(jìn)行篩選。如果數(shù)據(jù)庫(kù)知道最終用戶的標(biāo)識(shí),或通過修改對(duì)實(shí)際數(shù)據(jù)訪問邏輯的篩選將授權(quán)邏輯嵌入到應(yīng)用程序中(通常通過將相應(yīng)的項(xiàng)目添加到 SQL 語句的 Where 子句中),則可以通過利用本機(jī)數(shù)據(jù)庫(kù)授權(quán)來實(shí)現(xiàn)此目的。取決于應(yīng)用程序復(fù)雜性,或許可以開發(fā)一個(gè)使用元數(shù)據(jù)描述授權(quán)規(guī)則的自定義框架,以自動(dòng)將安全更改應(yīng)用到 SQL。如果將存儲(chǔ)過程用于數(shù)據(jù)訪問,則這些存儲(chǔ)過程也是應(yīng)用這些規(guī)則的最佳場(chǎng)所。
用戶界面自定義
動(dòng)態(tài)修改用戶界面,以僅向用戶顯示其能夠執(zhí)行的操作,這樣的做法常常也被視為授權(quán)的一個(gè)方面。雖然這很有爭(zhēng)議,但它無疑是一個(gè)常見的問題;向用戶顯示他們未被授權(quán)執(zhí)行的鏈接的做法肯定非常不好(并可能招致黑客攻擊)。
自定義 UI 以刪除鏈接和菜單選項(xiàng)的做法與基于角色的授權(quán)非常相似。這通常只是簡(jiǎn)單地使用 isUserInRole() 調(diào)用來遍歷各項(xiàng),從而驗(yàn)證用戶是否應(yīng)該看到此項(xiàng)。還可以采用相同的方式自定義表單上的實(shí)際數(shù)據(jù),不過這可能會(huì)導(dǎo)致進(jìn)行冗長(zhǎng)煩瑣的編程,而且不能提供足夠的靈活性,具體取決于業(yè)務(wù)需求。當(dāng)需要將訪問控制自定義與其他類型(如用戶首選項(xiàng))集成時(shí),這種方法可能會(huì)變得尤為復(fù)雜。如果用戶能夠訪問允許開發(fā)自己的特定 UI 組件視圖的某種首選項(xiàng)工具,則該工具也需要識(shí)別授權(quán)規(guī)則元數(shù)據(jù),以防止用戶向其個(gè)性化表單添加其不應(yīng)該具有訪問權(quán)限的項(xiàng)。正如前面討論自定義基于角色的授權(quán)時(shí)所提到的,可能會(huì)存在數(shù)據(jù)視圖與上下文相關(guān)的情況,并不能僅簡(jiǎn)單地基于靜態(tài)角色對(duì)其進(jìn)行調(diào)整。
【編輯推薦】