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