John Musser/Paul Feuer 著
Java Authentication Authorization Service(JAAS,Java驗(yàn)證和授權(quán)API)提供了靈活和可伸縮的機(jī)制來保證客戶端或服務(wù)器端的Java程序。Java早期的安全框架強(qiáng)調(diào)的是通過驗(yàn)證代碼的來源和作者,保護(hù)用戶避免受到下載下來的代碼的攻擊。JAAS強(qiáng)調(diào)的是通過驗(yàn)證誰在運(yùn)行代碼以及他/她的權(quán)限來保護(hù)系統(tǒng)面受用戶的攻擊。它讓你能夠?qū)⒁恍?biāo)準(zhǔn)的安全機(jī)制,例如Solaris NIS(網(wǎng)絡(luò)信息服務(wù))、Windows NT、LDAP(輕量目錄存取協(xié)議),Kerberos等通過一種通用的,可配置的方式集成到系統(tǒng)中。本文首先向你介紹JAAS驗(yàn)證中的一些核心部分,然后通過例子向你展示如何開發(fā)登錄模塊。
你是否曾經(jīng)需要為一個(gè)應(yīng)用程序?qū)崿F(xiàn)登錄模塊呢?如果你是一個(gè)比較有經(jīng)驗(yàn)的程序員,相信你這樣的工作做過很多次,而且每次都不完全一樣。你有可能把你的登錄模塊建立在Oracle數(shù)據(jù)庫的基礎(chǔ)上,也有可能使用的是NT的用戶驗(yàn)證,或者使用的是LDAP目錄。如果有一種方法可以在不改變應(yīng)用程序級的代碼的基礎(chǔ)上支持上面提到的所有這一些安全機(jī)制,對于程序員來說一定是一件幸運(yùn)的事。
現(xiàn)在你可以使用JAAS實(shí)現(xiàn)上面的目標(biāo)。JAAS是一個(gè)比較新的的Java API。在J2SE 1.3中,它是一個(gè)擴(kuò)展包;在J2SE 1.4中變成了一個(gè)核心包。在本文中,我們將介紹JAAS的一些核心概念,然后通過例子說明如何將JAAS應(yīng)用到實(shí)際的程序中。本文的例子是根據(jù)我們一個(gè)基于Web的Java應(yīng)用程序進(jìn)行改編的,在這個(gè)例子中,我們使用了關(guān)系數(shù)據(jù)庫保存用戶的登錄信息。由于使用了JAAS,我們實(shí)現(xiàn)了一個(gè)健壯而靈活的登錄和身份驗(yàn)證模塊。
Java驗(yàn)證和授權(quán):概論
在JAAS出現(xiàn)以前,Java的安全模型是為了滿足跨平臺的網(wǎng)絡(luò)應(yīng)用程序的需要而設(shè)計(jì)的。在Java早期版本中,Java通常是作為遠(yuǎn)程代碼被使用,例如Applet,。因此最初的安全模型把注意力放在通過驗(yàn)證代碼的來源來保護(hù)用戶上。早期的Java安全機(jī)制中包含的概念,如SercurityManager,沙箱概念,代碼簽名,策略文件,多是為了保護(hù)用戶。
JAAS的出現(xiàn)反映了Java的演變。傳統(tǒng)的服務(wù)器/客戶端程序需要實(shí)現(xiàn)登錄和存取控制,JAAS通過對運(yùn)行程序的用戶的進(jìn)行驗(yàn)證,從而達(dá)到保護(hù)系統(tǒng)的目的。雖然JAAS同時(shí)具有驗(yàn)證和授權(quán)的能力,在這篇文章中,我們主要介紹驗(yàn)證功能。
通過在應(yīng)用程序和底層的驗(yàn)證和授權(quán)機(jī)制之間加入一個(gè)抽象層,JAAS可以簡化涉及到Java Security包的程序開發(fā)。抽象層獨(dú)立于平臺的特性使開發(fā)人員可以使用各種不同的安全機(jī)制,而且不用修改應(yīng)用程序級的代碼。和其他Java Security API相似,JAAS通過一個(gè)可擴(kuò)展的框架:服務(wù)提供者接口(Service Provider Interface,SPI)來保證程序獨(dú)立于安全機(jī)制。服務(wù)提供者接口是由一組抽象類和接口組成的。圖一中給出了JAAS程序的整體框架圖。應(yīng)用程序級的代碼主要處理LoginContext。在LoginContext下面是一組動(dòng)態(tài)配置的LoginModules。LoginModule使用正確的安全機(jī)制進(jìn)行驗(yàn)證。
圖一給出了JAAS的概覽。應(yīng)用程序?qū)拥拇a只需要和LoginContext打交道。在LoginContext之下是一組動(dòng)態(tài)配置的LoginModule對象,這些對象使用相關(guān)的安全基礎(chǔ)結(jié)構(gòu)進(jìn)行驗(yàn)證操作。
JAAS提供了一些LoginModule的參考實(shí)現(xiàn)代碼,比如JndiLoginModule。開發(fā)人員也可以自己實(shí)現(xiàn)LoginModule接口,就象在我們例子中的RdbmsLonginModule。同時(shí)我們還會(huì)告訴你如何使用一個(gè)簡單的配置文件來安裝應(yīng)用程序。
為了滿足可插接性,JAAS是可堆疊的。在單一登錄的情況下,一組安全模塊可以堆疊在一起,然后被其他的安全機(jī)制按照堆疊的順序被調(diào)用。
JAAS的實(shí)現(xiàn)者根據(jù)現(xiàn)在一些流行的安全結(jié)構(gòu)模式和框架將JASS模型化。例如可堆疊的特性同Unix下的可堆疊驗(yàn)證模塊(PAM,Pluggable Authentication Module)框架就非常相似。從事務(wù)的角度看,JAAS類似于雙步提交(Two-Phase Commit,2PC)協(xié)議的行為。JAAS中安全配置的概念(包括策略文件(Police File)和許可(Permission))來自于J2SE 1.2。JAAS還從其他成熟的安全框架中借鑒了許多思想。
客戶端和服務(wù)器端的JAAS
開發(fā)人員可以將JAAS應(yīng)用到客戶端和服務(wù)器端。在客戶端使用JAAS很簡單。在服務(wù)器端使用JAAS時(shí)情況要復(fù)雜一些。目前在應(yīng)用服務(wù)器市場中的JAAS產(chǎn)品還不是很一致,使用JAAS的J2EE應(yīng)用服務(wù)器有一些細(xì)微的差別。例如JBossSx使用自己的結(jié)構(gòu),將JAAS集成到了一個(gè)更大的安全框架中;而雖然WebLogic 6.x也使用了JAAS,安全框架卻完全不一樣。
現(xiàn)在你能夠理解為什么我們需要從客戶端和服務(wù)器端的角度來看JAAS了。我們將在后面列出兩種情況下的例子。為了使服務(wù)器端的例子程序更加簡單,我們使用了Resin應(yīng)用服務(wù)器。
核心JAAS類
在使用JAAS之前,你首先需要安裝JAAS。在J2SE 1.4中已經(jīng)包括了JAAS,但是在J2SE 1.3中沒有。如果你希望使用J2SE 1.3,你可以從SUN的官方站點(diǎn)上下載JAAS。當(dāng)正確安裝了JAAS后,你會(huì)在安裝目錄的lib目錄下找到j(luò)aas.jar。你需要將該路徑加入Classpath中。(注:如果你安裝了應(yīng)用服務(wù)器,其中就已經(jīng)包括了JAAS,請閱讀應(yīng)用服務(wù)器的幫助文檔以獲得更詳細(xì)的信息)。在Java安全屬性文件java.security中,你可以改變一些與JAAS相關(guān)的系統(tǒng)屬性。該文件保存在<jre_home>/lib/security目錄中。
在應(yīng)用程序中使用JAAS驗(yàn)證通常會(huì)涉及到以下幾個(gè)步驟:
1. 創(chuàng)建一個(gè)LoginContext的實(shí)例。
2. 為了能夠獲得和處理驗(yàn)證信息,將一個(gè)CallBackHandler對象作為參數(shù)傳送給LoginContext。
3. 通過調(diào)用LoginContext的login()方法來進(jìn)行驗(yàn)證。
4. 通過使用login()方法返回的Subject對象實(shí)現(xiàn)一些特殊的功能(假設(shè)登錄成功)。
下面是一個(gè)簡單的例子:
在運(yùn)行這段代碼時(shí),后臺進(jìn)行了以下的工作。
1. 當(dāng)初始化時(shí),LoginContext對象首先在JAAS配置文件中找到MyExample項(xiàng),然后更具該項(xiàng)的內(nèi)容決定該加載哪個(gè)LoginModule對象(參見圖二)。
2. 在登錄時(shí),LoginContext對象調(diào)用每個(gè)LoginModule對象的login()方法。
3. 每個(gè)login()方法進(jìn)行驗(yàn)證操作或獲得一個(gè)CallbackHandle對象。
4. CallbackHandle對象通過使用一個(gè)或多個(gè)CallBack方法同用戶進(jìn)行交互,獲得用戶輸入。
5. 向一個(gè)新的Subject對象中填入驗(yàn)證信息。
我們將對代碼作進(jìn)一步的解釋。但是在這之前,讓我們先看代碼中涉及到的核心JAAS類和接口。這些類可以被分為三種類型:
普通類型 Subject,Principal,憑證
驗(yàn)證 LoginContext,LoginModule,CallBackHandler,Callback
授權(quán) Policy,AuthPermission,PrivateCredentialPermission
上面列舉的類和接口大多數(shù)都在javax.security.auth包中。在J2SE 1.4中,還有一些接口的實(shí)現(xiàn)類在com.sun.security.auth包中。
普通類型:Subject,Principal,憑證
Subject類代表了一個(gè)驗(yàn)證實(shí)體,它可以是用戶、管理員、Web服務(wù),設(shè)備或者其他的過程。該類包含了三中類型的安全信息:
身份(Identities):由一個(gè)或多個(gè)Principal對象表示
公共憑證(Public credentials):例如名稱或公共秘鑰
私有憑證(Private credentials):例如口令或私有密鑰
Principal對象代表了Subject對象的身份。它們實(shí)現(xiàn)了java.security.Principal和java.io.Serializable接口。在Subject類中,最重要的方法是getName()。該方法返回一個(gè)身份名稱。在Subject對象中包含了多個(gè)Principal對象,因此它可以擁有多個(gè)名稱。由于登錄名稱、身份證號和Email地址都可以作為用戶的身份標(biāo)識,可見擁有多個(gè)身份名稱的情況在實(shí)際應(yīng)用中是非常普遍的情況。
在上面提到的憑證并不是一個(gè)特定的類或借口,它可以是任何對象。憑證中可以包含任何特定安全系統(tǒng)需要的驗(yàn)證信息,例如標(biāo)簽(ticket),密鑰或口令。Subject對象中維護(hù)著一組特定的私有和公有的憑證,這些憑證可以通過getPrivateCredentials()和getPublicCredentials()方法獲得。這些方法通常在應(yīng)用程序?qū)又械陌踩酉到y(tǒng)被調(diào)用。
驗(yàn)證:LoginContext
在應(yīng)用程序?qū)又校憧梢允褂肔oginContext對象來驗(yàn)證Subject對象。LoginContext對象同時(shí)體現(xiàn)了JAAS的動(dòng)態(tài)可插入性(Dynamic Pluggability),因?yàn)楫?dāng)你創(chuàng)建一個(gè)LoginContext的實(shí)例時(shí),你需要指定一個(gè)配置。LoginContext通常從一個(gè)文本文件中加載配置信息,這些配置信息告訴LoginContext對象在登錄時(shí)使用哪一個(gè)LoginModule對象。
下面列出了在LoginContext中經(jīng)常使用的三個(gè)方法:
login () 進(jìn)行登錄操作。該方法激活了配置中制定的所有LoginModule對象。如果成功,它將創(chuàng)建一個(gè)經(jīng)過了驗(yàn)證的Subject對象;否則拋出LoginException異常。
getSubject () 返回經(jīng)過驗(yàn)證的Subject對象
logout () 注銷Subject對象,刪除與之相關(guān)的Principal對象和憑證
驗(yàn)證:LoginModule
LoginModule是調(diào)用特定驗(yàn)證機(jī)制的接口。J2EE 1.4中包含了下面幾種LoginModule的實(shí)現(xiàn)類:
JndiLoginModule 用于驗(yàn)證在JNDI中配置的目錄服務(wù)
Krb5LoginModule 使用Kerberos協(xié)議進(jìn)行驗(yàn)證
NTLoginModul 使用當(dāng)前用戶在NT中的用戶信息進(jìn)行驗(yàn)證
UnixLoginModule 使用當(dāng)前用戶在Unix中的用戶信息進(jìn)行驗(yàn)證
同上面這些模塊綁定在一起的還有對應(yīng)的Principal接口的實(shí)現(xiàn)類,例如NTDomainPrincipal和UnixPrincipal。這些類在com.sun.security.auth包中。
LoginModule接口中包含了五個(gè)方法:
initialize () 當(dāng)創(chuàng)建一LoginModule實(shí)例時(shí)會(huì)被構(gòu)造函數(shù)調(diào)用
login () 進(jìn)行驗(yàn)證
commit () 當(dāng)LgoninContext對象接受所有LoginModule對象傳回的結(jié)果后將調(diào)用該方法。該方法將Principal對象和憑證賦給Subject對象。
abort () 當(dāng)任何一個(gè)LoginModule對象驗(yàn)證失敗時(shí)都會(huì)調(diào)用該方法。此時(shí)沒有任何Principal對象或憑證關(guān)聯(lián)到Subject對象上。
logout () 刪除與Subject對象關(guān)聯(lián)的Principal對象和憑證。
在應(yīng)用程序的代碼中,程序員通常不會(huì)直接調(diào)用上面列出的方法,而是通過LigonContext間接調(diào)用這些方法。
驗(yàn)證:CallbackHandler和Callback
CallbackHandler和Callback對象可以使LoginModule對象從系統(tǒng)和用戶那里收集必要的驗(yàn)證信息,同時(shí)獨(dú)立于實(shí)際的收集信息時(shí)發(fā)生的交互過程。
JAAS在javax.sevurity.auth.callback包中包含了七個(gè)Callback的實(shí)現(xiàn)類和兩個(gè)CallbackHandler的實(shí)現(xiàn)類:ChoiceCallback、ConfirmationCallback、LogcaleCallback、NameCallback、PasswordCallback、TextInputCallback、TextOutputCallback、DialogCallbackHandler和TextCallBackHandler。Callback接口只會(huì)在客戶端會(huì)被使用到。我將在后面介紹如何編寫你自己的CallbackHandler類。
配置文件
上面我已經(jīng)提到,JAAS的可擴(kuò)展性來源于它能夠進(jìn)行動(dòng)態(tài)配置,而配置信息通常是保存在文本。這些文本文件有很多個(gè)配置塊構(gòu)成,我們通常把這些配置塊稱作申請(Application)。每個(gè)申請對應(yīng)了一個(gè)或多個(gè)特定的LoginModule對象。
當(dāng)你的代碼構(gòu)造一個(gè)LoginContext對象時(shí),你需要把配置文件中申請的名稱傳遞給它。LoginContext將會(huì)根據(jù)申請中的信息決定激活哪些LoginModule對象,按照什么順序激活以及使用什么規(guī)則激活。
配置文件的結(jié)構(gòu)如下所示:
下面是一個(gè)名稱為Sample的申請
上面這個(gè)簡單的申請指定了LoginContext對象應(yīng)該使用NTLoginModule進(jìn)行驗(yàn)證。類的名稱在ModuleClass中被指定。Flag控制當(dāng)申請中包含了多個(gè)LoginModule時(shí)進(jìn)行登錄時(shí)的行為:Required、Sufficient、Requisite和Optional。最常用的是Required,使用它意味著對應(yīng)的LoginModule對象必須被調(diào)用,并且必須需要通過所有的驗(yàn)證。由于Flag本身的復(fù)雜性,本文在這里不作深究。
ModuleOption允許有多個(gè)參數(shù)。例如你可以設(shè)定調(diào)試參數(shù)為True(debug=true),這樣診斷輸出將被送到System.out中。
配置文件可以被任意命名,并且可以被放在任何位置。JAAS框架通過使用java.securty.auth.long.config屬性來確定配置文件的位置。例如當(dāng)你的應(yīng)用程序是JaasTest,配置文件是當(dāng)前目錄下的jaas.config,你需要在命令行中輸入:
java -Djava.security.auth.login.config=jass.config JavaTest
圖二描述了配置文件中各元素之間的關(guān)系
通過命令行方式進(jìn)行登錄驗(yàn)證
為了說明JAAS到底能干什么,我在這里編寫了兩個(gè)例子。一個(gè)是簡單的由命令行輸入調(diào)用的程序,另一個(gè)是服務(wù)器端的JSP程序。這兩個(gè)程序都通過用戶名/密碼的方式進(jìn)行登錄,然后使用關(guān)系數(shù)據(jù)庫對其進(jìn)行驗(yàn)證。
為了通過數(shù)據(jù)庫進(jìn)行驗(yàn)證,我們需要:
1. 實(shí)現(xiàn)RdbmsLoginModul類,該類可以對輸入的信息進(jìn)行驗(yàn)證。
2. 編輯一個(gè)配置文件,告訴LoginContext如何使用RdbmsLoginModule。
3. 實(shí)現(xiàn)ConsoleCallbackHandler類,通過該類可以獲取用戶的輸入。
4. 編寫應(yīng)用程序代碼。
在RdbmsLoginModul類中,我們必須實(shí)現(xiàn)LgoinModule接口中的五個(gè)方法。首先是initialize()方法:
LoginContext在調(diào)用login()方法時(shí)會(huì)調(diào)用initialize()方法。RdbmsLoginModule的第一個(gè)任務(wù)就是在類中保存輸入?yún)?shù)的引用。在驗(yàn)證成功后將向Subject對象中送入Principal對象和憑證。
CallbackHandler對象將會(huì)在login()方法中被使用到。sharedState可以使數(shù)據(jù)在不同的LoginModule對象之間共享,但是在這個(gè)例子中我們不會(huì)使用它。最后是名為options的Map對象。options向LgoinModule對象傳遞在配置文件ModuleOption域中定義的參數(shù)的值。配置文件如下所示:
在配置文件中,RdbmsLoginModule包含了五個(gè)參數(shù),其中driver、url、user和password是必需的,而debug是可選闡述。driver、url、user和password參數(shù)告訴我們?nèi)绾潍@得JDBC連接。當(dāng)然你還可以在ModuleOption域中加入數(shù)據(jù)庫中的表或列的信息。使用這些參數(shù)的目的是為了能夠?qū)?shù)據(jù)庫進(jìn)行操作。在LoginModule類的initialize()方法中我們保存了每個(gè)參數(shù)的值。
我們前面提到一個(gè)LoginContext對應(yīng)的配置文件告訴它應(yīng)該使用文件中的哪一個(gè)申請。這個(gè)信息是通過LgoinContext的構(gòu)造函數(shù)傳遞的。下面是初始化客戶端的代碼,在代碼中創(chuàng)建了一個(gè)LoginContex對象并調(diào)用了login()方法。
當(dāng)LgoinContext.login()方法被調(diào)用時(shí),它調(diào)用所有加載了的LoginModule對象的login()方法。在我們的這個(gè)例子中是RdbmsLoginModule中的login()方法。
RdbmsLoginModule中的login()方法進(jìn)行了下面的操作:
1. 創(chuàng)建兩個(gè)Callback對象。這些對象從用戶輸入中獲取用戶名/密碼。程序中使用了JAAS中的兩個(gè)Callback類:NameCallback和PasswordCallback(這兩個(gè)類包含在javax.security.auth.callback包中)。
2. 通過將callbacks作為參數(shù)傳遞給CallbackHandler的handle()方法來激活Callback。
3. 通過Callback對象獲得用戶名/密碼。
4. 在rdbmsValidate()方法中通過JDBC在數(shù)據(jù)庫中驗(yàn)證獲取的用戶名/密碼。
下面是RdbmsLoginModule中的login()方法的代碼
在ConsoleCallbackHandler類的handle()方法中你可以看到Callback對象是如何同用戶進(jìn)行交互的:
使用JSP和關(guān)系數(shù)據(jù)庫進(jìn)行登錄驗(yàn)證
現(xiàn)在我們希望將通過命令行調(diào)用的程序一直到Web應(yīng)用程序中。由于Web應(yīng)用程序與一般的應(yīng)用程序的交互方式有區(qū)別不同,我們將不能使用JAAS提供的標(biāo)準(zhǔn)Callback和CallbackHandler類。因?yàn)槲覀儾荒茉赪eb程序中打開一個(gè)命令窗口讓用戶輸入信息。也許你會(huì)想到我們也可以使用基于HTTP的驗(yàn)證,這樣我們可以從瀏覽器彈出的用戶名/密碼窗口中獲得用戶輸入。但是這樣做也有一些問題,它要求建立起雙向的HTTP連接(在login()方法很難實(shí)現(xiàn)雙向連接)。因此在我們的例子中我們會(huì)使用利用表單進(jìn)行登錄,從表單中獲取用戶輸入的信息,然后通過RdbmsLoginModule類驗(yàn)證它。
由于我們沒有在應(yīng)用層同LoginModule直接打交道,而是通過LgoinContext來調(diào)用其中的方法的,我們?nèi)绾螌@得用戶名和密碼放入LoginModule對象中呢?我們可以使用其它的方法來繞過這個(gè)問題,例如我們可以在創(chuàng)建LoginContext對象前先初始化一個(gè)Subject對象,在Subject對象的憑證中保存用戶名和密碼。然后我們可以將該Subject對象傳遞給LoginContext的構(gòu)造函數(shù)。這種方法雖然從技術(shù)上來說沒有什么問題,但是它在應(yīng)用程序?qū)釉黾恿撕芏嗯c安全機(jī)制相關(guān)的代碼。而且通常是在驗(yàn)證后向Subject送入憑證,而不是之前。
前面我們提到可以實(shí)現(xiàn)一個(gè)CallbackHandler類,然后將它的實(shí)例傳遞給LoginContext對象。在這里我們可以采用類似的方法來處理用戶名和密碼。我們實(shí)現(xiàn)了一個(gè)新的類PassiveCallbackHandler。下面是在JSP中使用該類的代碼:
PassiveCallbackHandler中構(gòu)造函數(shù)的參數(shù)包含了用戶名和密碼。因此它可以在Callbick對象中設(shè)定正確的值。下面是PassiveCallbackHandler類的handle()方法的代碼:
從上面的代碼中可以發(fā)現(xiàn)實(shí)際上我們只是從ConsoleCallbackHandler中去除了那些提示用戶輸入的代碼。
在運(yùn)行這個(gè)JSP例子的時(shí)候,我們需要設(shè)定系統(tǒng)屬性,這樣LgoinContext對象才知道如何查找名稱為"Example"的配置。我們使用的是Resin服務(wù)器。在resin.conf中,我們增加了一個(gè)<caucho.com>節(jié)點(diǎn):
小結(jié)
JAAS通過提供動(dòng)態(tài)的、可擴(kuò)展的模型來進(jìn)行用戶驗(yàn)證和控制權(quán)限,從而使應(yīng)用程序有更加健壯的安全機(jī)制。同時(shí)它還能夠讓你能夠很輕松地創(chuàng)建自己的登錄機(jī)制。JAAS可以同時(shí)在在客戶端和服務(wù)器端應(yīng)用程序上工作。雖然在服務(wù)器端的JAAS到目前還不是很穩(wěn)定,但是隨著技術(shù)的發(fā)展,相信會(huì)很好地解決這個(gè)問題。
Java Authentication Authorization Service(JAAS,Java驗(yàn)證和授權(quán)API)提供了靈活和可伸縮的機(jī)制來保證客戶端或服務(wù)器端的Java程序。Java早期的安全框架強(qiáng)調(diào)的是通過驗(yàn)證代碼的來源和作者,保護(hù)用戶避免受到下載下來的代碼的攻擊。JAAS強(qiáng)調(diào)的是通過驗(yàn)證誰在運(yùn)行代碼以及他/她的權(quán)限來保護(hù)系統(tǒng)面受用戶的攻擊。它讓你能夠?qū)⒁恍?biāo)準(zhǔn)的安全機(jī)制,例如Solaris NIS(網(wǎng)絡(luò)信息服務(wù))、Windows NT、LDAP(輕量目錄存取協(xié)議),Kerberos等通過一種通用的,可配置的方式集成到系統(tǒng)中。本文首先向你介紹JAAS驗(yàn)證中的一些核心部分,然后通過例子向你展示如何開發(fā)登錄模塊。
你是否曾經(jīng)需要為一個(gè)應(yīng)用程序?qū)崿F(xiàn)登錄模塊呢?如果你是一個(gè)比較有經(jīng)驗(yàn)的程序員,相信你這樣的工作做過很多次,而且每次都不完全一樣。你有可能把你的登錄模塊建立在Oracle數(shù)據(jù)庫的基礎(chǔ)上,也有可能使用的是NT的用戶驗(yàn)證,或者使用的是LDAP目錄。如果有一種方法可以在不改變應(yīng)用程序級的代碼的基礎(chǔ)上支持上面提到的所有這一些安全機(jī)制,對于程序員來說一定是一件幸運(yùn)的事。
現(xiàn)在你可以使用JAAS實(shí)現(xiàn)上面的目標(biāo)。JAAS是一個(gè)比較新的的Java API。在J2SE 1.3中,它是一個(gè)擴(kuò)展包;在J2SE 1.4中變成了一個(gè)核心包。在本文中,我們將介紹JAAS的一些核心概念,然后通過例子說明如何將JAAS應(yīng)用到實(shí)際的程序中。本文的例子是根據(jù)我們一個(gè)基于Web的Java應(yīng)用程序進(jìn)行改編的,在這個(gè)例子中,我們使用了關(guān)系數(shù)據(jù)庫保存用戶的登錄信息。由于使用了JAAS,我們實(shí)現(xiàn)了一個(gè)健壯而靈活的登錄和身份驗(yàn)證模塊。
Java驗(yàn)證和授權(quán):概論
在JAAS出現(xiàn)以前,Java的安全模型是為了滿足跨平臺的網(wǎng)絡(luò)應(yīng)用程序的需要而設(shè)計(jì)的。在Java早期版本中,Java通常是作為遠(yuǎn)程代碼被使用,例如Applet,。因此最初的安全模型把注意力放在通過驗(yàn)證代碼的來源來保護(hù)用戶上。早期的Java安全機(jī)制中包含的概念,如SercurityManager,沙箱概念,代碼簽名,策略文件,多是為了保護(hù)用戶。
JAAS的出現(xiàn)反映了Java的演變。傳統(tǒng)的服務(wù)器/客戶端程序需要實(shí)現(xiàn)登錄和存取控制,JAAS通過對運(yùn)行程序的用戶的進(jìn)行驗(yàn)證,從而達(dá)到保護(hù)系統(tǒng)的目的。雖然JAAS同時(shí)具有驗(yàn)證和授權(quán)的能力,在這篇文章中,我們主要介紹驗(yàn)證功能。
通過在應(yīng)用程序和底層的驗(yàn)證和授權(quán)機(jī)制之間加入一個(gè)抽象層,JAAS可以簡化涉及到Java Security包的程序開發(fā)。抽象層獨(dú)立于平臺的特性使開發(fā)人員可以使用各種不同的安全機(jī)制,而且不用修改應(yīng)用程序級的代碼。和其他Java Security API相似,JAAS通過一個(gè)可擴(kuò)展的框架:服務(wù)提供者接口(Service Provider Interface,SPI)來保證程序獨(dú)立于安全機(jī)制。服務(wù)提供者接口是由一組抽象類和接口組成的。圖一中給出了JAAS程序的整體框架圖。應(yīng)用程序級的代碼主要處理LoginContext。在LoginContext下面是一組動(dòng)態(tài)配置的LoginModules。LoginModule使用正確的安全機(jī)制進(jìn)行驗(yàn)證。
圖一給出了JAAS的概覽。應(yīng)用程序?qū)拥拇a只需要和LoginContext打交道。在LoginContext之下是一組動(dòng)態(tài)配置的LoginModule對象,這些對象使用相關(guān)的安全基礎(chǔ)結(jié)構(gòu)進(jìn)行驗(yàn)證操作。
![]() 圖一 JAAS概覽 |
JAAS提供了一些LoginModule的參考實(shí)現(xiàn)代碼,比如JndiLoginModule。開發(fā)人員也可以自己實(shí)現(xiàn)LoginModule接口,就象在我們例子中的RdbmsLonginModule。同時(shí)我們還會(huì)告訴你如何使用一個(gè)簡單的配置文件來安裝應(yīng)用程序。
為了滿足可插接性,JAAS是可堆疊的。在單一登錄的情況下,一組安全模塊可以堆疊在一起,然后被其他的安全機(jī)制按照堆疊的順序被調(diào)用。
JAAS的實(shí)現(xiàn)者根據(jù)現(xiàn)在一些流行的安全結(jié)構(gòu)模式和框架將JASS模型化。例如可堆疊的特性同Unix下的可堆疊驗(yàn)證模塊(PAM,Pluggable Authentication Module)框架就非常相似。從事務(wù)的角度看,JAAS類似于雙步提交(Two-Phase Commit,2PC)協(xié)議的行為。JAAS中安全配置的概念(包括策略文件(Police File)和許可(Permission))來自于J2SE 1.2。JAAS還從其他成熟的安全框架中借鑒了許多思想。
客戶端和服務(wù)器端的JAAS
開發(fā)人員可以將JAAS應(yīng)用到客戶端和服務(wù)器端。在客戶端使用JAAS很簡單。在服務(wù)器端使用JAAS時(shí)情況要復(fù)雜一些。目前在應(yīng)用服務(wù)器市場中的JAAS產(chǎn)品還不是很一致,使用JAAS的J2EE應(yīng)用服務(wù)器有一些細(xì)微的差別。例如JBossSx使用自己的結(jié)構(gòu),將JAAS集成到了一個(gè)更大的安全框架中;而雖然WebLogic 6.x也使用了JAAS,安全框架卻完全不一樣。
現(xiàn)在你能夠理解為什么我們需要從客戶端和服務(wù)器端的角度來看JAAS了。我們將在后面列出兩種情況下的例子。為了使服務(wù)器端的例子程序更加簡單,我們使用了Resin應(yīng)用服務(wù)器。
核心JAAS類
在使用JAAS之前,你首先需要安裝JAAS。在J2SE 1.4中已經(jīng)包括了JAAS,但是在J2SE 1.3中沒有。如果你希望使用J2SE 1.3,你可以從SUN的官方站點(diǎn)上下載JAAS。當(dāng)正確安裝了JAAS后,你會(huì)在安裝目錄的lib目錄下找到j(luò)aas.jar。你需要將該路徑加入Classpath中。(注:如果你安裝了應(yīng)用服務(wù)器,其中就已經(jīng)包括了JAAS,請閱讀應(yīng)用服務(wù)器的幫助文檔以獲得更詳細(xì)的信息)。在Java安全屬性文件java.security中,你可以改變一些與JAAS相關(guān)的系統(tǒng)屬性。該文件保存在<jre_home>/lib/security目錄中。
在應(yīng)用程序中使用JAAS驗(yàn)證通常會(huì)涉及到以下幾個(gè)步驟:
1. 創(chuàng)建一個(gè)LoginContext的實(shí)例。
2. 為了能夠獲得和處理驗(yàn)證信息,將一個(gè)CallBackHandler對象作為參數(shù)傳送給LoginContext。
3. 通過調(diào)用LoginContext的login()方法來進(jìn)行驗(yàn)證。
4. 通過使用login()方法返回的Subject對象實(shí)現(xiàn)一些特殊的功能(假設(shè)登錄成功)。
下面是一個(gè)簡單的例子:
LoginContext lc = new LoginContext("MyExample"); try { lc.login(); } catch (LoginException) { // Authentication failed. } // Authentication successful, we can now continue. // We can use the returned Subject if we like. Subject sub = lc.getSubject(); Subject.doAs(sub, new MyPrivilegedAction()); |
在運(yùn)行這段代碼時(shí),后臺進(jìn)行了以下的工作。
1. 當(dāng)初始化時(shí),LoginContext對象首先在JAAS配置文件中找到MyExample項(xiàng),然后更具該項(xiàng)的內(nèi)容決定該加載哪個(gè)LoginModule對象(參見圖二)。
2. 在登錄時(shí),LoginContext對象調(diào)用每個(gè)LoginModule對象的login()方法。
3. 每個(gè)login()方法進(jìn)行驗(yàn)證操作或獲得一個(gè)CallbackHandle對象。
4. CallbackHandle對象通過使用一個(gè)或多個(gè)CallBack方法同用戶進(jìn)行交互,獲得用戶輸入。
5. 向一個(gè)新的Subject對象中填入驗(yàn)證信息。
我們將對代碼作進(jìn)一步的解釋。但是在這之前,讓我們先看代碼中涉及到的核心JAAS類和接口。這些類可以被分為三種類型:
普通類型 Subject,Principal,憑證
驗(yàn)證 LoginContext,LoginModule,CallBackHandler,Callback
授權(quán) Policy,AuthPermission,PrivateCredentialPermission
上面列舉的類和接口大多數(shù)都在javax.security.auth包中。在J2SE 1.4中,還有一些接口的實(shí)現(xiàn)類在com.sun.security.auth包中。
普通類型:Subject,Principal,憑證
Subject類代表了一個(gè)驗(yàn)證實(shí)體,它可以是用戶、管理員、Web服務(wù),設(shè)備或者其他的過程。該類包含了三中類型的安全信息:
身份(Identities):由一個(gè)或多個(gè)Principal對象表示
公共憑證(Public credentials):例如名稱或公共秘鑰
私有憑證(Private credentials):例如口令或私有密鑰
Principal對象代表了Subject對象的身份。它們實(shí)現(xiàn)了java.security.Principal和java.io.Serializable接口。在Subject類中,最重要的方法是getName()。該方法返回一個(gè)身份名稱。在Subject對象中包含了多個(gè)Principal對象,因此它可以擁有多個(gè)名稱。由于登錄名稱、身份證號和Email地址都可以作為用戶的身份標(biāo)識,可見擁有多個(gè)身份名稱的情況在實(shí)際應(yīng)用中是非常普遍的情況。
在上面提到的憑證并不是一個(gè)特定的類或借口,它可以是任何對象。憑證中可以包含任何特定安全系統(tǒng)需要的驗(yàn)證信息,例如標(biāo)簽(ticket),密鑰或口令。Subject對象中維護(hù)著一組特定的私有和公有的憑證,這些憑證可以通過getPrivateCredentials()和getPublicCredentials()方法獲得。這些方法通常在應(yīng)用程序?qū)又械陌踩酉到y(tǒng)被調(diào)用。
驗(yàn)證:LoginContext
在應(yīng)用程序?qū)又校憧梢允褂肔oginContext對象來驗(yàn)證Subject對象。LoginContext對象同時(shí)體現(xiàn)了JAAS的動(dòng)態(tài)可插入性(Dynamic Pluggability),因?yàn)楫?dāng)你創(chuàng)建一個(gè)LoginContext的實(shí)例時(shí),你需要指定一個(gè)配置。LoginContext通常從一個(gè)文本文件中加載配置信息,這些配置信息告訴LoginContext對象在登錄時(shí)使用哪一個(gè)LoginModule對象。
下面列出了在LoginContext中經(jīng)常使用的三個(gè)方法:
login () 進(jìn)行登錄操作。該方法激活了配置中制定的所有LoginModule對象。如果成功,它將創(chuàng)建一個(gè)經(jīng)過了驗(yàn)證的Subject對象;否則拋出LoginException異常。
getSubject () 返回經(jīng)過驗(yàn)證的Subject對象
logout () 注銷Subject對象,刪除與之相關(guān)的Principal對象和憑證
驗(yàn)證:LoginModule
LoginModule是調(diào)用特定驗(yàn)證機(jī)制的接口。J2EE 1.4中包含了下面幾種LoginModule的實(shí)現(xiàn)類:
JndiLoginModule 用于驗(yàn)證在JNDI中配置的目錄服務(wù)
Krb5LoginModule 使用Kerberos協(xié)議進(jìn)行驗(yàn)證
NTLoginModul 使用當(dāng)前用戶在NT中的用戶信息進(jìn)行驗(yàn)證
UnixLoginModule 使用當(dāng)前用戶在Unix中的用戶信息進(jìn)行驗(yàn)證
同上面這些模塊綁定在一起的還有對應(yīng)的Principal接口的實(shí)現(xiàn)類,例如NTDomainPrincipal和UnixPrincipal。這些類在com.sun.security.auth包中。
LoginModule接口中包含了五個(gè)方法:
initialize () 當(dāng)創(chuàng)建一LoginModule實(shí)例時(shí)會(huì)被構(gòu)造函數(shù)調(diào)用
login () 進(jìn)行驗(yàn)證
commit () 當(dāng)LgoninContext對象接受所有LoginModule對象傳回的結(jié)果后將調(diào)用該方法。該方法將Principal對象和憑證賦給Subject對象。
abort () 當(dāng)任何一個(gè)LoginModule對象驗(yàn)證失敗時(shí)都會(huì)調(diào)用該方法。此時(shí)沒有任何Principal對象或憑證關(guān)聯(lián)到Subject對象上。
logout () 刪除與Subject對象關(guān)聯(lián)的Principal對象和憑證。
在應(yīng)用程序的代碼中,程序員通常不會(huì)直接調(diào)用上面列出的方法,而是通過LigonContext間接調(diào)用這些方法。
驗(yàn)證:CallbackHandler和Callback
CallbackHandler和Callback對象可以使LoginModule對象從系統(tǒng)和用戶那里收集必要的驗(yàn)證信息,同時(shí)獨(dú)立于實(shí)際的收集信息時(shí)發(fā)生的交互過程。
JAAS在javax.sevurity.auth.callback包中包含了七個(gè)Callback的實(shí)現(xiàn)類和兩個(gè)CallbackHandler的實(shí)現(xiàn)類:ChoiceCallback、ConfirmationCallback、LogcaleCallback、NameCallback、PasswordCallback、TextInputCallback、TextOutputCallback、DialogCallbackHandler和TextCallBackHandler。Callback接口只會(huì)在客戶端會(huì)被使用到。我將在后面介紹如何編寫你自己的CallbackHandler類。
配置文件
上面我已經(jīng)提到,JAAS的可擴(kuò)展性來源于它能夠進(jìn)行動(dòng)態(tài)配置,而配置信息通常是保存在文本。這些文本文件有很多個(gè)配置塊構(gòu)成,我們通常把這些配置塊稱作申請(Application)。每個(gè)申請對應(yīng)了一個(gè)或多個(gè)特定的LoginModule對象。
當(dāng)你的代碼構(gòu)造一個(gè)LoginContext對象時(shí),你需要把配置文件中申請的名稱傳遞給它。LoginContext將會(huì)根據(jù)申請中的信息決定激活哪些LoginModule對象,按照什么順序激活以及使用什么規(guī)則激活。
配置文件的結(jié)構(gòu)如下所示:
Application { ModuleClass Flag ModuleOptions; ModuleClass Flag ModuleOptions; ... }; Application { ModuleClass Flag ModuleOptions; ... }; ... |
下面是一個(gè)名稱為Sample的申請
Sample { com.sun.security.auth.module.NTLoginModule Rquired debug=true; } |
上面這個(gè)簡單的申請指定了LoginContext對象應(yīng)該使用NTLoginModule進(jìn)行驗(yàn)證。類的名稱在ModuleClass中被指定。Flag控制當(dāng)申請中包含了多個(gè)LoginModule時(shí)進(jìn)行登錄時(shí)的行為:Required、Sufficient、Requisite和Optional。最常用的是Required,使用它意味著對應(yīng)的LoginModule對象必須被調(diào)用,并且必須需要通過所有的驗(yàn)證。由于Flag本身的復(fù)雜性,本文在這里不作深究。
ModuleOption允許有多個(gè)參數(shù)。例如你可以設(shè)定調(diào)試參數(shù)為True(debug=true),這樣診斷輸出將被送到System.out中。
配置文件可以被任意命名,并且可以被放在任何位置。JAAS框架通過使用java.securty.auth.long.config屬性來確定配置文件的位置。例如當(dāng)你的應(yīng)用程序是JaasTest,配置文件是當(dāng)前目錄下的jaas.config,你需要在命令行中輸入:
java -Djava.security.auth.login.config=jass.config JavaTest
圖二描述了配置文件中各元素之間的關(guān)系
![]() 圖二 JAAS的配置文件 |
通過命令行方式進(jìn)行登錄驗(yàn)證
為了說明JAAS到底能干什么,我在這里編寫了兩個(gè)例子。一個(gè)是簡單的由命令行輸入調(diào)用的程序,另一個(gè)是服務(wù)器端的JSP程序。這兩個(gè)程序都通過用戶名/密碼的方式進(jìn)行登錄,然后使用關(guān)系數(shù)據(jù)庫對其進(jìn)行驗(yàn)證。
為了通過數(shù)據(jù)庫進(jìn)行驗(yàn)證,我們需要:
1. 實(shí)現(xiàn)RdbmsLoginModul類,該類可以對輸入的信息進(jìn)行驗(yàn)證。
2. 編輯一個(gè)配置文件,告訴LoginContext如何使用RdbmsLoginModule。
3. 實(shí)現(xiàn)ConsoleCallbackHandler類,通過該類可以獲取用戶的輸入。
4. 編寫應(yīng)用程序代碼。
在RdbmsLoginModul類中,我們必須實(shí)現(xiàn)LgoinModule接口中的五個(gè)方法。首先是initialize()方法:
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; url = (String)options.get("url"); driverClass = (String)options.get("driver"); debug = "true".equalsIgnoreCase((String)options.get("debug")); } |
LoginContext在調(diào)用login()方法時(shí)會(huì)調(diào)用initialize()方法。RdbmsLoginModule的第一個(gè)任務(wù)就是在類中保存輸入?yún)?shù)的引用。在驗(yàn)證成功后將向Subject對象中送入Principal對象和憑證。
CallbackHandler對象將會(huì)在login()方法中被使用到。sharedState可以使數(shù)據(jù)在不同的LoginModule對象之間共享,但是在這個(gè)例子中我們不會(huì)使用它。最后是名為options的Map對象。options向LgoinModule對象傳遞在配置文件ModuleOption域中定義的參數(shù)的值。配置文件如下所示:
Example { RdbmsLoginModule required driver="org.gjt.mm.mysql.Driver" url="jdbc:mysql://localhost/jaasdb?user=root" debug="true"; }; |
在配置文件中,RdbmsLoginModule包含了五個(gè)參數(shù),其中driver、url、user和password是必需的,而debug是可選闡述。driver、url、user和password參數(shù)告訴我們?nèi)绾潍@得JDBC連接。當(dāng)然你還可以在ModuleOption域中加入數(shù)據(jù)庫中的表或列的信息。使用這些參數(shù)的目的是為了能夠?qū)?shù)據(jù)庫進(jìn)行操作。在LoginModule類的initialize()方法中我們保存了每個(gè)參數(shù)的值。
我們前面提到一個(gè)LoginContext對應(yīng)的配置文件告訴它應(yīng)該使用文件中的哪一個(gè)申請。這個(gè)信息是通過LgoinContext的構(gòu)造函數(shù)傳遞的。下面是初始化客戶端的代碼,在代碼中創(chuàng)建了一個(gè)LoginContex對象并調(diào)用了login()方法。
ConsoleCallbackHandler cbh = new ConsoleCallbackHandler(); LoginContext lc = new LoginContext("Example", cbh); lc.login(); |
當(dāng)LgoinContext.login()方法被調(diào)用時(shí),它調(diào)用所有加載了的LoginModule對象的login()方法。在我們的這個(gè)例子中是RdbmsLoginModule中的login()方法。
RdbmsLoginModule中的login()方法進(jìn)行了下面的操作:
1. 創(chuàng)建兩個(gè)Callback對象。這些對象從用戶輸入中獲取用戶名/密碼。程序中使用了JAAS中的兩個(gè)Callback類:NameCallback和PasswordCallback(這兩個(gè)類包含在javax.security.auth.callback包中)。
2. 通過將callbacks作為參數(shù)傳遞給CallbackHandler的handle()方法來激活Callback。
3. 通過Callback對象獲得用戶名/密碼。
4. 在rdbmsValidate()方法中通過JDBC在數(shù)據(jù)庫中驗(yàn)證獲取的用戶名/密碼。
下面是RdbmsLoginModule中的login()方法的代碼
public boolean login() throws LoginException { if (callbackHandler == null) throw new LoginException("no handler"); NameCallback nameCb = new NameCallback("user: "); PasswordCallback passCb = new PasswordCallback("password: ", true); callbacks = new Callback[] { nameCb, passCb }; callbackHandler.handle(callbacks); String username = nameCb.getName(); String password = new String(passCb.getPassword()); success = rdbmsValidate(username, password); return(true); } |
在ConsoleCallbackHandler類的handle()方法中你可以看到Callback對象是如何同用戶進(jìn)行交互的:
public void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof NameCallback) { NameCallback nameCb = (NameCallback)callbacks[i]; System.out.print(nameCb.getPrompt()); String user=(new BufferedReader(new InputStreamReader(System.in))).readLine(); nameCb.setName(user); } else if (callbacks[i] instanceof PasswordCallback) { PasswordCallback passCb = (PasswordCallback)callbacks[i]; System.out.print(passCb.getPrompt()); String pass=(new BufferedReader(new InputStreamReader(System.in))).readLine(); passCb.setPassword(pass.toCharArray()); } else { throw(new UnsupportedCallbackException(callbacks[i], "Callback class not supported")); } } } |
使用JSP和關(guān)系數(shù)據(jù)庫進(jìn)行登錄驗(yàn)證
現(xiàn)在我們希望將通過命令行調(diào)用的程序一直到Web應(yīng)用程序中。由于Web應(yīng)用程序與一般的應(yīng)用程序的交互方式有區(qū)別不同,我們將不能使用JAAS提供的標(biāo)準(zhǔn)Callback和CallbackHandler類。因?yàn)槲覀儾荒茉赪eb程序中打開一個(gè)命令窗口讓用戶輸入信息。也許你會(huì)想到我們也可以使用基于HTTP的驗(yàn)證,這樣我們可以從瀏覽器彈出的用戶名/密碼窗口中獲得用戶輸入。但是這樣做也有一些問題,它要求建立起雙向的HTTP連接(在login()方法很難實(shí)現(xiàn)雙向連接)。因此在我們的例子中我們會(huì)使用利用表單進(jìn)行登錄,從表單中獲取用戶輸入的信息,然后通過RdbmsLoginModule類驗(yàn)證它。
由于我們沒有在應(yīng)用層同LoginModule直接打交道,而是通過LgoinContext來調(diào)用其中的方法的,我們?nèi)绾螌@得用戶名和密碼放入LoginModule對象中呢?我們可以使用其它的方法來繞過這個(gè)問題,例如我們可以在創(chuàng)建LoginContext對象前先初始化一個(gè)Subject對象,在Subject對象的憑證中保存用戶名和密碼。然后我們可以將該Subject對象傳遞給LoginContext的構(gòu)造函數(shù)。這種方法雖然從技術(shù)上來說沒有什么問題,但是它在應(yīng)用程序?qū)釉黾恿撕芏嗯c安全機(jī)制相關(guān)的代碼。而且通常是在驗(yàn)證后向Subject送入憑證,而不是之前。
前面我們提到可以實(shí)現(xiàn)一個(gè)CallbackHandler類,然后將它的實(shí)例傳遞給LoginContext對象。在這里我們可以采用類似的方法來處理用戶名和密碼。我們實(shí)現(xiàn)了一個(gè)新的類PassiveCallbackHandler。下面是在JSP中使用該類的代碼:
String user = request.getParameter("user"); String pass = request.getParameter("pass"); PassiveCallbackHandler cbh = new PassiveCallbackHandler(user, pass); LoginContext lc = new LoginContext("Example", cbh); |
PassiveCallbackHandler中構(gòu)造函數(shù)的參數(shù)包含了用戶名和密碼。因此它可以在Callbick對象中設(shè)定正確的值。下面是PassiveCallbackHandler類的handle()方法的代碼:
public void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof NameCallback) { NameCallback nameCb = (NameCallback)callbacks[i]; nameCb.setName(user); } else if(callbacks[i] instanceof PasswordCallback) { PasswordCallback passCb = (PasswordCallback)callbacks[i]; passCb.setPassword(pass.toCharArray()); } else { throw(new UnsupportedCallbackException(callbacks[i], "Callback class not supported")); } } } } |
從上面的代碼中可以發(fā)現(xiàn)實(shí)際上我們只是從ConsoleCallbackHandler中去除了那些提示用戶輸入的代碼。
在運(yùn)行這個(gè)JSP例子的時(shí)候,我們需要設(shè)定系統(tǒng)屬性,這樣LgoinContext對象才知道如何查找名稱為"Example"的配置。我們使用的是Resin服務(wù)器。在resin.conf中,我們增加了一個(gè)<caucho.com>節(jié)點(diǎn):
<system-property java.security.auth.login.config="/resin/conf/jaas.config"/> |
小結(jié)
JAAS通過提供動(dòng)態(tài)的、可擴(kuò)展的模型來進(jìn)行用戶驗(yàn)證和控制權(quán)限,從而使應(yīng)用程序有更加健壯的安全機(jī)制。同時(shí)它還能夠讓你能夠很輕松地創(chuàng)建自己的登錄機(jī)制。JAAS可以同時(shí)在在客戶端和服務(wù)器端應(yīng)用程序上工作。雖然在服務(wù)器端的JAAS到目前還不是很穩(wěn)定,但是隨著技術(shù)的發(fā)展,相信會(huì)很好地解決這個(gè)問題。