擴展JAAS
Guosheng Huang, PhD, is a seniorsoftware developer withWysdom Inc. He has over 15years of experience in software engineering and technical architecture. [email protected]
翻譯:綠野風(fēng)煙 2003/10
用戶認證和訪問控制是大多數(shù)java應(yīng)用的重要安全尺度,,特別是J2EE應(yīng)用。Java認證和權(quán)限服務(wù)(即JAAS),J2SE1.4和1.5的核心API,描繪表達了新的安全標準,。其提供了一個可插拔的(pluggable)和富有彈性的(flexible)框架(framework)允許開發(fā)者混合不同的安全機制和豐富的已經(jīng)存在各種安全方面的資源。
伴隨著即將來臨的J2SE1.5版本的發(fā)布,,它包含了許多諸如加密技術(shù),、XML安全性、公鑰機制(PKI),、Kerberos (是一個網(wǎng)絡(luò)附加系統(tǒng)/協(xié)議,,可以允許用戶通過一個安全伺服器的服務(wù)來驗證 自己。象遠端登陸,,遠端拷貝,,系統(tǒng)間的相互檔拷貝和另外高風(fēng)險任務(wù)的服務(wù)將被變 得相當(dāng)安全和可控制。)和結(jié)盟認證(the federating identity)的增強,!,,JAAS將會在J2EE實現(xiàn)中扮演一個更加重要的角色。
認證
認證就是校驗一個用戶擁有使用已經(jīng)被企業(yè)用戶注冊機構(gòu)證明了的身份鑒定的權(quán)限的處理過程,。JAAS的認證機制建立于一整套可插拔的模塊(參看圖1)基礎(chǔ)上,。JAAS允許不同的驗證模型在運行時可被插拔,??蛻魬?yīng)用總是通過登陸上下文對象和JAAS交互。
認證處理過程典型的要經(jīng)過下面的步驟:
1,、 生成一個LoginContext對象,。這個LoginContext尋找配置文件以決定使用那個LoginModule。同樣,,可選擇的,,有可能傳遞一個CallbackHandler給LoginContext.
2、 通過調(diào)用LoginContext的login方法執(zhí)行認證,,它會加載預(yù)定義的LoginModule去檢驗是否用戶可以被認證,。
3、 如果用戶被認證,那么用規(guī)則和標識和其所屬項進行關(guān)聯(lián),。
4,、 或者在登陸失敗的情況下跑出一個LoginException
5、 使用LoginContext的logout方法進行注銷登陸
在JAAS中,,登陸是一個兩階段(two-phase)的處理過程,。第一階段是“登陸(login)”階段(就像上面2所描述的)。這個階段唯一的任務(wù)是認證,。只要處理過程成功通過這個階段,,認證處理過程就進入了“提交(commit)”階段(如上步驟3),這一階段LoginModule的commit方法被調(diào)用去關(guān)聯(lián)所屬子項相關(guān)的規(guī)則和標識,。
在JAAS中一個所屬子項表示一個認證實體,,比如一個人或者一臺設(shè)備。它包含了一整套法則和安全相關(guān)的屬性諸如密碼和加密密鑰,。在JAAS體系結(jié)構(gòu)中,,所屬子項和其所附屬的相關(guān)權(quán)限,扮演了重要的角色在認證過程當(dāng)中,。所有的認證模塊當(dāng)中,,LoginModule是事實上的認證機制的借口。雖然LoginModule決沒有得到直接調(diào)用客戶應(yīng)用的機會,,但是他經(jīng)由一個可插拔的模塊提供了一個認證的具體類型,,其實現(xiàn)了認證的算法并且決定實際的認證過程是怎樣被執(zhí)行的。
SUN提供了幾個默認的LoginModule 實現(xiàn),,在sun.com.security.auth.module包里有諸如JndiLoginModule,Krb2LoginModule,UnixLoginModule和NTLoginModule等幾個LoginModule實現(xiàn),。因為JAAS登錄結(jié)構(gòu)體系是可擴展的,所以你只要在配置文件中指定使用哪個LoginModule模塊就可以幾乎全部插入任何LoginModule模塊,。
如下即為一個配置文件的例子:
MySample {
com.sample.module.MyLoginModule required debug=true;
};
這里MySample是登錄上下文環(huán)境(login context)的名字,,當(dāng)你生成一個新的LoginContext開始認證過程時它會被傳入LoginContex的構(gòu)造函數(shù)中。依據(jù)配置塊提示,,那個文本塊提醒JAAS有關(guān)LoginModule在登錄過程中應(yīng)該被用來執(zhí)行認證,。另外,對于LoginModule,,任何關(guān)于他的選項也可以在這里被指定,。在執(zhí)行登錄這一步驟的過程中,CallbackHandler類被LoginModule類用來跟用戶通信已便于取得認證信息,。CallbackHandler類處理三種類型的回調(diào)(Callback):NameCallback,提示用戶輸入一個用戶名,;PasswordCallcack,提示輸入密碼;TextOutputCallback,報告錯誤,、警告或則發(fā)送給用戶一些其他信息,。
授權(quán)是決定是否認證的用戶可以執(zhí)行一些動作的工作,,例如訪問一處資源。因為JAAS建立于已經(jīng)存在的Java安全模型的基礎(chǔ)上,,故這個過程時基于策略的,。策略配置文件實質(zhì)上包含了一系列的入口,諸如“Keystore”和/或“grant”.
grant入口包含了所有的權(quán)限,,他是通過認證的代碼或則法則被授予可以進行安全敏感的操作,,例如,訪問一個具體的Web頁面或則本地的文件,。JAAS支持基于法則的策略入口,,賦權(quán)入口基本格式如下:
grant Codebase “codebase_URL” Signedby “signer_name,”
Principal principal_class_name “principal_name”,
Principal principal_class_name “principal_name”,
… {
permission permission_class_name “target_name”, “action”,
permission permission_class_name “target_name”, “action”,
…
}
上面格式中“動作(action)”可能是必需的或則可能被忽略依賴于權(quán)限類型。在JAAS體系結(jié)構(gòu)中,,策略對象表達了一個Java應(yīng)用環(huán)境的系統(tǒng)安全策略和在任何時間事實上只有一個策略對象,。依據(jù)Java2 SDK文檔,默認的策略實現(xiàn)是sun.security.provider.PolicyFile,其中策略被指定在一個或多個策略配置文件里,。
只要用戶被認證,,授權(quán)經(jīng)由Subject.doAs方法發(fā)生,或者從Subject類的靜態(tài)方法doAsPrivileged,,doAS方法用當(dāng)前的AccessControlContext動態(tài)和子項并且同時調(diào)用run方法去執(zhí)行動作,,他導(dǎo)致安全驗證。權(quán)限驗證過程通過下面的步驟在圖2:
就像LoginModule,策略也是可插拔的模型,。你可以掛上其它的策略實現(xiàn)通過在java.security的屬性文件中改變“policy.provider=sun.security.provider.PolicyFile”
到一個你項使用的策略類,。
Extend JAAS
JAAS建立于已經(jīng)存在的Java安全模型的頂端,其基于“CodeSource”和平面文本格式策略文件實現(xiàn),。這可能對企業(yè)應(yīng)用是不夠用的,,你可能想使用可定制的安全倉庫。對于JAAS的其它實現(xiàn),諸如LDAP(輕型目錄訪問協(xié)議),,數(shù)據(jù)庫或者其他文件系統(tǒng),,它可以通過編寫你自己的可定制模塊被完成,感謝JAAS的可插拔的特性,。然而,,這需要對模塊和JAAS中的處理過程有完善的理解,同時你必須做許多編碼去覆寫相關(guān)的類,,并且處理好配置和策略兩種文件,。
理想情況下,,我們愿意能夠擴展JAAS以一個更加容易的方式以便于無論何時一個可定制的安全知識庫或者不同的訪問控制機制改變或者必須去增加時,,你能夠只開發(fā)和插入這些不同的小模塊(即,適配器)去適應(yīng)這些新的變化和需求,,并且在最好的情況下,,不必去理解和熟悉JAAS處理過程的細節(jié),,同樣,我們也愿意能夠去做這些變化僅僅通過改變一個配置文件,。另一個目標是我們的JAAS擴展組件能夠被使用在不同的J2EE應(yīng)用中—獨立的或者Web上的,。圖3描述了JAAS擴展組件的設(shè)計意圖。我們的JAAS擴展組在實現(xiàn)可定制的LoginModule和策略模塊時充分件利用了JAAS插拔式的體系結(jié)構(gòu),。這些模塊中,,我們委派數(shù)據(jù)請求到適配器。這些適配器的每個對于諸如數(shù)據(jù)取回的簡單任務(wù)是隔離的,,所以你可以快速地使用不同的安全知識或者算法開發(fā)不同的適配器而不是嘗試去實現(xiàn)不同的LoginModule或者策略模塊,,它們更加復(fù)雜并且需要更多的努力。
你可以從www.sys-con.com/java/sourcec.cfm.下在完整的源瑪,。
實現(xiàn)的AuthLoginModule類
AuthLoginModule類是我們定制的LoginModule實現(xiàn),,LoginModule類是在JAAS中是一個可插拔組件并且服務(wù)于兩個目的:
1、鑒定認證用戶
2,、如果認證成公,,則用相關(guān)的負責(zé)人信息或者證書更新主題。
LoginModule有5個方法去實現(xiàn)功能,,讓我們關(guān)注一下login()方法,。這個方法被調(diào)用以認證主題并且主要作兩件事情:
1、包含用戶名和密碼,,典型地,,LoginModule要調(diào)用CallbackHandler類的handle方法去得到用戶名和密碼
2、通過和數(shù)據(jù)源中的比較校驗密碼,。LoginModule從Callbacks取回用戶名和密碼,。(其默認期望用戶接口的某種排序),這一點對于一個簡單的演示程序或者就在命令行,,可是他對于一個J2EE應(yīng)用來說不太實用,,例如,對于大多數(shù)的Web應(yīng)用,,用戶名和密碼將比較典型的從一個form中讀出,。在這種情況下,使用JAAS認證會比較困難,??紤]我們不直接使用LoginModule,解決方案是實現(xiàn)一個可定制的CallbackHandler類,他會接收用戶名和密碼然后遞交它們給LoginModule,,所以他沒有必要提示用戶輸入信息
以下示例説明用戶信息如何從JSP或者Servlet中傳遞:
String userName = request.getParameter (“user”);
String password = request.getParameter(“password”);
LoginContext context = new LoginContext (“MySample”,
new AuthCallbackHandler (userName, password));
一旦擁有了用戶名和密碼在手,,AuthLoginModule類,我們的LoginModule類的定制實現(xiàn),,會經(jīng)由LoginSourceAdapterFactory實例化LoginSourceAdapter并同時委派實際的認證過程到資源適配器,。適配器只不過是一個簡單的類,,其從一個具體的數(shù)據(jù)適配器(比如數(shù)據(jù)庫或者LDAP,或者一些別的系統(tǒng))領(lǐng)取用戶信息。在“提交”階段,,AuthLoginModule類從LoginSourceAdapter類取回相關(guān)的信息并且把他們和主題相關(guān)聯(lián),。
LoginSourceAdapter類
LoginSourceAdapter類是一個認證目的的資源適配器的接口,它有4個需要實現(xiàn)的方法:
1,、void initialize(Hashtable parameters):initialized方法被調(diào)用來以相關(guān)的參數(shù)初始化適配器,。此方法在對象生成后立即被調(diào)用并且優(yōu)先于任何對其他方法的調(diào)用。
2,、boolean authenticate(String username,char[] password):此認證方法被調(diào)用來認證用戶,。
3. String[] getGroupNames (String userName):getGroupNames方法被調(diào)用來在認證成功后得到相關(guān)的主要信息。
4. void terminate ():這個方法在LoginModule類的logout方法被執(zhí)行后調(diào)用,,它給適配器做一些清理工作的機會,。
AuthPolicy類
在JAAS架構(gòu)下,安全策略被java.securety.Policy 類來處理,,他會證明賦給一個具體的代碼源或者主體的多種權(quán)限,。就像在上一段被討論的,sun.securety.provider.PolicyFile是其默認實現(xiàn),。PolicyFile類使用平面文本文件去證明在權(quán)限和代碼源之間的對應(yīng)關(guān)系,,這點對于企業(yè)級應(yīng)用可能不是太好。一個集中的系統(tǒng)比如支持基于角色的安全性的關(guān)系數(shù)據(jù)庫將會更好,。很明顯,,擴展JAAS授權(quán)以處理不同的來源的不同的安全標記,我們需要寫我們自己的策略實現(xiàn),。
生成一個定制的策略實現(xiàn)的步驟如下:
•擴展java.securety.Policy類
•實現(xiàn)getPermissions()方法
•實現(xiàn)refresh()方法.
如果你看到我們定制的策略類的實現(xiàn),,你可能注意到我們的AuthPolicy類派生在sun.security.provider.PolicyFile而不是java.security.Policy. 為什么?首先,,我想要實現(xiàn)AuthPolicy類作為通用的Policy類,,這可以處理默認的策略類不需要用任何適配器介入。通過從PolicyFile類,,我們不需要去實現(xiàn)策略文件解析和其他相關(guān)的代碼,。同時,黨應(yīng)用運行于一個安全管理器起作用的情況下,,一些權(quán)限,,比如doAsPrivileged AuthPermission類和讀入配置文件的FilePermission(為了載入配置文件),需要被賦權(quán)為了執(zhí)行JAAS.
當(dāng)然,這些權(quán)限可以被存儲在數(shù)據(jù)源里,,但是把它們放入標準Java安全策略文件中可能更為有利,。可是,對于正規(guī)開發(fā)你應(yīng)該實現(xiàn)一個適配器以應(yīng)付這些事情,。在擴展認證時要遵循相同的設(shè)計模式。我們的策略類委托權(quán)限請求于
PermisssionAdapter類
在權(quán)限類里,,不同的權(quán)限保存于自己的PermissionCollection類實例,,如果你創(chuàng)建一個定制的權(quán)限類,你需要生成你自己的PermissionCollection類類型,,否則不能保證你的權(quán)限對象將被參考確認,。
PermissionAdapter類
PermissionAdapter類在我們的JAAS擴展組件中是認證過程可插拔模塊的接口。它從一個具體的數(shù)據(jù)源評估策略并且分發(fā)一個包含一套已賦予的權(quán)限的PermissionCollection類,。PermissionAdapter類接口有下面的方法:
• void initialize (Hashtable initParams):initialize方法被調(diào)用以相關(guān)的參數(shù)初始化適配器,。此方法會被立即調(diào)用并且優(yōu)先于任何其它的方法調(diào)用。同時,,在Policy類的refresh方法被執(zhí)行后它也會被調(diào)用,。
• PermissionCollection getPermissions (ProtectionDomain
domain): 本方法只要某種主體權(quán)限被請求就會被調(diào)用。
作為一例子,,讓我們看看如何實現(xiàn)一個基于角色的PermissionAdapter類,。假設(shè)有三個角色:管理員,用戶,,和客人,,都擁有不同的權(quán)限,并且所有的權(quán)限信息被存儲在數(shù)據(jù)庫中,。
首先,,在initialize方法中,我們將從數(shù)據(jù)中取得所有角色的權(quán)限信息并且組裝入集合類中,,比如,,Hashtable.接下來,在getPermissions方法中,,我們會收集到相關(guān)主體有關(guān)的權(quán)限(這是僅有的會涉及到基于角色的訪問控制)并且返回它們,。注意我們可以得到相關(guān)的主體通過調(diào)用ProtectedDomain類的getPrincipals方法。它是如此的簡單,,不是嗎,?
JaasUtil類
對于我們的JAAS擴展組件JaasUtil類是主要的紐帶,并且它有一個構(gòu)造函數(shù)取得用戶名和密碼,。有兩個關(guān)鍵的方法:
1. boolean authenticate()
2. boolean checkPermission(Subject subject,final Permission perm)
JaasUtil類實際延遲了LoginContext類的登陸請求和SecurityManager類的權(quán)限檢查步驟,。 Listing 1 顯示了如何使用JaasUtil類。這段代碼首先從HtttpServletRequest類取得用戶名和密碼并且嘗試認證用戶.然后其檢測是否用戶有權(quán)限訪問“editReg.jsp”.
配置
現(xiàn)在我們擁有自己的定制的LoginModule,Policy和其他相關(guān)模塊實現(xiàn),。這些模塊可以委托相關(guān)的數(shù)據(jù)請求給合適的適配器,;這是很好的事情。然而,,在JAAS結(jié)構(gòu)中LoginModule類和Policy類絕對不會被應(yīng)用程序直接調(diào)用,,所以我們怎樣知道那種適配器應(yīng)該被實力化和怎樣傳遞需要的參數(shù)或者信息,,比如數(shù)據(jù)連接,給適配器,?答案是適配器可以通過更新一個XML配置文件被動態(tài)配置,。這個XML配置文件由兩個主要的數(shù)據(jù)段組成:
1、<authentication>:本段內(nèi)容定義登陸源適配器和認證過程需要的各種可能的輸入?yún)?shù),。
2,、<authorization>:本段內(nèi)容定義權(quán)限適配器和授權(quán)過程序要的各種可能的輸入?yún)?shù)
你可以制定使用哪個LoginSourceAdapter類和PermissionAdapter類。在配置文件中傳遞額外的信息也是可能的,。讓JaasUtil類知道在那里尋找配置文件有兩種途徑:
1,、 制定配置文件經(jīng)由命令行屬性開關(guān): -Dcon.auth.config
2、 調(diào)用JaasUtil.setConfigFile(configFile)方法.
當(dāng)你部署JAAS擴展組件時,,這個定制安全策略類文件必須被加入到Java的jre/lib目錄,,這將引起策略類文件被bootstrap類加載其載入。否則,,即便你放置策略文件在你的Java類路徑中,,它也將不能被檢出并且Sun默認提供的策略類將會代替而被使用。
總結(jié)
擴展JAAS是不困難的,。JAAS結(jié)構(gòu)提供給你定制實現(xiàn)認證和授權(quán)過程的彈性,。理解這些過程如何工作是懂得如何替換你自己的實現(xiàn)的第一步。在本文中,,我們重溫JAAS的基礎(chǔ),,并且檢查驗證了如何擴展JAAS使其成為一個更加具備動態(tài)化、靈活化和規(guī)?;奶匦缘目蚣?。使用這種擴展框架,你既能夠輕松的生成自己登陸和訪問控制機制以支持你自己的企業(yè)級別的安全需求也能夠支持新興的安全標準,,或者平衡你們已經(jīng)存在的或定制安全模型作為適配器,,然后熱插拔他們進入JAAS中。這應(yīng)該可以給你的企業(yè)應(yīng)用提供一個基于標準的同時高可定制的認證和授權(quán)過程,。