術(shù)語中英對照
| 縮寫 | 全拼 | 中文解釋 |
|---|---|---|
| JAAS | Java Authentication and Authorization Service | Java身份驗證和授權(quán)服務(wù) |
| PAM | Pluggable Authentication Module | 可插拔身份驗證模塊 |
概覽
JAAS 代表Java身份驗證和授權(quán)服務(wù),它被用于兩個目的:
- 用戶認(rèn)證,可靠且安全地確定當(dāng)前是誰在執(zhí)行Java代碼
- 用戶授權(quán),確保他們擁有執(zhí)行操作所需的訪問控制權(quán)限。
JAAS實現(xiàn)了標(biāo)準(zhǔn)可插拔身份驗證模塊(PAM)框架的Java版本
傳統(tǒng)的java提供了基于codesource-based 訪問控制(訪問控制基于代碼的來源和誰簽名了該代碼)。但是,它缺乏根據(jù)誰運行代碼來附加強制訪問控制的能力。JAAS提供了一個框架,通過這種支持來增強Java安全體系結(jié)構(gòu)。
JAAS身份驗證是以可插入的方式執(zhí)行的。這允許應(yīng)用程序獨立于底層身份驗證技術(shù),可以在應(yīng)用程序下插入新的或更新的身份驗證技術(shù),而不需要修改應(yīng)用程序本身。應(yīng)用程序通過實例化LoginContext對象來啟用身份驗證過程,而LoginContext對象又引用Configuration 或 LoginModule,用于執(zhí)行身份驗證。典型的LoginModules可能會提示輸入和驗證用戶名和密碼
執(zhí)行代碼的用戶或服務(wù)經(jīng)過身份驗證后,JAAS授權(quán)組件將與核心Java SE訪問控制模型一起工作,以保護對敏感資源的訪問。訪問控制決策既基于執(zhí)行代碼的CodeSource,也基于運行代碼的用戶或服務(wù),后者由Subject對象表示,如果身份驗證成功,則使用相關(guān)Principals和credentials 的LoginModule更新Subject。
核心類和接口
與jaas相關(guān)的核心類和接口可以分為三類:Common, Authentication, and Authorization.
Common 類
公共類是由JAAS身份驗證和授權(quán)組件共享的類。關(guān)鍵的JAAS類是javax.security.auth.Subject,它表示單個實體(如人)的相關(guān)信息的分組。它包含實體的Principals、公共credentials和私有credentials
Subject
要授權(quán)訪問資源,應(yīng)用程序首先需要對請求的源進行身份驗證。JAAS框架定義了表示請求源的術(shù)語subject。主體可以是任何實體,例如人或服務(wù)。一旦對主題進行了身份驗證,則使用javax.security.auth.Subject由關(guān)聯(lián)的標(biāo)識或Principals填充。一個subject可能有多個principal。例如,一個人可能有一個name Principal(“John Doe”)和一個SSN Principal(“123-45-6789”)
subject還可以擁有與安全性相關(guān)的屬性,這些屬性稱為credentials憑據(jù),敏感的credentials比如私鑰使用Set<Object> privCredentials表示,共享非敏感credentials比如公鑰使用Set<Object> pubCredentials表示。訪問和修改不同的憑據(jù)集需要不同的權(quán)限(如下所述):
subject是使用這些構(gòu)造函數(shù)創(chuàng)建的:
public Subject();
public Subject(boolean readOnly, Set principals,
Set pubCredentials, Set privCredentials);
第一個構(gòu)造函數(shù)創(chuàng)建了一個principal和credential為空(非空)的subject。第二個構(gòu)造函數(shù)使用指定的principals和credentials創(chuàng)建subject。它還有一個布爾參數(shù),可用于使subject為只讀。在只讀subject中,principals和credentials是不可變的。
應(yīng)用程序編寫者不必實例化subject。如果應(yīng)用程序?qū)嵗艘粋€LoginContext,而沒有將Subject傳遞給LoginContext構(gòu)造函數(shù),那么LoginContext實例化了一個新的空Subject
如果沒有在實例化Subject使其處于只讀狀態(tài),則可以通過調(diào)用以下方法將其設(shè)置為只讀:
public boolean isReadOnly();
要獲取與subject相關(guān)的principals,有兩種方法:
public Set getPrincipals();
public Set getPrincipals(Class c);
第一個方法返回subject中的所有principals,而第二個方法只返回指定類c的實例或類c的子類的實例化的principals。如果subject沒有任何關(guān)聯(lián)的principals,則返回一個空集合。
獲取subject的公有憑證和私有憑證的方式和獲取主體的方式類似
public Set getPublicCredentials();
public Set getPublicCredentials(Class c);
public Set getPrivateCredentials();
public Set getPrivateCredentials(Class c);
要修改或操作subject的principals、pubCredentials或privCredentials,調(diào)用者使用java.util.Set中定義的方法。下面的示例演示了這一點:
Subject subject;
Principal principal;
Object credential;
. . .
// add a Principal and credential to the Subject
subject.getPrincipals().add(principal);
subject.getPublicCredentials().add(credential);
主題可以與AccessControlContext關(guān)聯(lián),下面的方法返回與指定AccessControlContext關(guān)聯(lián)的subject,如果沒有與指定AccessControlContext關(guān)聯(lián)的subject,則返回null。
public static Subject getSubject(final AccessControlContext acc);
作為特定Subject執(zhí)行操作的doAs方法
可以調(diào)用下列靜態(tài)方法來執(zhí)行作為特定Subject的操作:
public static Object
doAs(final Subject subject,
final java.security.PrivilegedAction action);
public static Object
doAs(final Subject subject,
final java.security.PrivilegedExceptionAction action)
throws java.security.PrivilegedActionException;
這兩個方法首先將指定的Subject與當(dāng)前線程的AccessControlContext關(guān)聯(lián),然后執(zhí)行操作。這實現(xiàn)了將動作作為Subject運行的效果。第一個方法可以拋出運行時異常,但正常執(zhí)行時它必須從其action參數(shù)的run方法返回一個對象,第二個方法的行為類似,只是它可以從PrivilegedExceptionAction的 run方法中拋出一個已檢查的異常
Subject.doAs Example
假設(shè)名為“Bob”的人已經(jīng)通過LoginContext(請參閱LoginContext)進行了身份驗證,因此,Subject 中填充了com.ibm.security.Principal類的Principal。這個Principal的名字叫BOB,還假設(shè)已經(jīng)安裝了SecurityManager,并且訪問控制策略中存在以下內(nèi)容:
// grant "BOB" permission to read the file "foo.txt"
grant Principal com.ibm.security.Principal "BOB" {
permission java.io.FilePermission "foo.txt", "read";
};
下面是示例應(yīng)用程序代碼
class ExampleAction implements java.security.PrivilegedAction {
public Object run() {
java.io.File f = new java.io.File("foo.txt");
// the following call invokes a security check
if (f.exists()) {
System.out.println("File foo.txt exists");
}
return null;
}
}
public class Example1 {
public static void main(String[] args) {
// Authenticate the subject, "BOB".
// This process is described in the
// LoginContext class.
Subject bob;
// Set bob to the Subject created during the
// authentication process
// perform "ExampleAction" as "BOB"
Subject.doAs(bob, new ExampleAction());
}
}
在執(zhí)行過程中,ExampleAction調(diào)用f.exists()時將遇到安全檢查。但是,由于ExampleAction作為“BOB”運行,并且策略(上面)將必要的文件權(quán)限授予“BOB”,因此ExampleAction將通過安全檢查。如果策略中的grant語句被更改(例如,添加不正確的CodeBase 或?qū)⒅黧w更改為“MOE”),則會拋出SecurityException。
doAsPrivileged methods
public static Object doAsPrivileged(
final Subject subject,
final java.security.PrivilegedAction action,
final java.security.AccessControlContext acc);
public static Object doAsPrivileged(
final Subject subject,
final java.security.PrivilegedExceptionAction action,
final java.security.AccessControlContext acc)
throws java.security.PrivilegedActionException;
doAs與doAsPrivileged
doasprivilege方法的行為與doAs方法完全相同,只是它需要通過傳入一個AccessControlContext而不是將提供的subject與當(dāng)前線程的AccessControlContext關(guān)聯(lián)。通過這種方式,可以通過與當(dāng)前上下文不同的AccessControlContext來控制操作。
AccessControlContext包含自實例化AccessControlContext以來執(zhí)行的所有代碼的信息,包括代碼位置和策略授予代碼的權(quán)限。為了使訪問控制檢查成功,策略必須為AccessControlContext引用的每個代碼項授予所需的權(quán)限。
如果提供給doasprivilege的AccessControlContext為null,則操作不受單獨的AccessControlContext的限制。比如在服務(wù)器環(huán)境中。服務(wù)器可以對多個傳入請求進行身份驗證,并為每個請求執(zhí)行單獨的doAs操作。要啟動每個doAs操作,并且不受當(dāng)前服務(wù)器AccessControlContext的限制,服務(wù)器可以調(diào)用doAsPrivileged并傳入null的AccessControlContext。
Principals
正如前面提到的,旦對主題進行了身份驗證,則使用javax.security.auth.Subject由關(guān)聯(lián)的標(biāo)識或Principals填充。一個subject可能有多個principal。例如,一個人可能有一個name Principal(“John Doe”)和一個SSN Principal(“123-45-6789”)。主體必須實現(xiàn)java.security.Principal和java.io.Serializable的接口
Credentials
除了關(guān)聯(lián)的主體外,主題還可以擁有與安全性相關(guān)的屬性,這些屬性稱為憑據(jù)。憑據(jù)可能包含用于向新服務(wù)驗證主題的信息。這些憑證包括密碼、Kerberos票據(jù)和公鑰證書。憑據(jù)還可能包含僅使主體能夠執(zhí)行某些活動的數(shù)據(jù)。例如,加密密鑰表示使主體能夠簽名或加密數(shù)據(jù)的憑據(jù)。公共憑證類和私有憑證類不是核心JAAS類庫的一部分。因此,任何類都可以表示憑證
:公共憑證類和私有憑證類不是核心JAAS類庫的一部分。然而,開發(fā)人員可以選擇讓他們的憑據(jù)類實現(xiàn)兩個與憑據(jù)相關(guān)的接口:Refreshable 和Destroyable。
Refreshable
javax.security.auth.Refreshable接口提供了憑據(jù)刷新自身的功能。例如,具有特定時間限制生命周期的憑據(jù)可以實現(xiàn)此接口,以允許調(diào)用者在其有效刷新刷新。該接口有兩種抽象方法:
boolean isCurrent();
此方法確定憑據(jù)是當(dāng)前的還是有效的
void refresh() throws RefreshFailedException;
此方法更新或擴展憑據(jù)的有效性
Destroyable
javax.security.auth.Destroyable接口提供了銷毀憑據(jù)內(nèi)內(nèi)容的功能。該接口有兩個抽象方法
boolean isDestroyed();
確定憑據(jù)是否已被銷毀
void destroy() throws DestroyFailedException;
銷毀并清除與此憑據(jù)關(guān)聯(lián)的信息。對該憑證上的某些方法的后續(xù)調(diào)用將導(dǎo)致拋出IllegalStateException
Authentication 類和接口
認(rèn)證是驗證subject身份的過程,必須以安全的方式執(zhí)行;否則,犯罪者可能會冒充他人來訪問系統(tǒng)。身份驗證通常涉及到subject展示某種形式的證據(jù)來證明其身份。這些證據(jù)可能是只有受試者可能知道或擁有的信息(例如密碼或指紋),也可能是只有受試者能夠生成的信息(例如使用私鑰簽名的數(shù)據(jù))。
要對subject(用戶或服務(wù))進行身份驗證,需要執(zhí)行以下步驟:
- 應(yīng)用程序?qū)嵗?code>LoginContext
-
LoginContext查詢一個Configuration,以加載為該應(yīng)用程序配置的所有LoginModules。 - 應(yīng)用程序調(diào)用
LoginContext的login方法 -
login方法調(diào)用所有加載的LoginModules。每個LoginModule都嘗試對subject進行身份驗證,成功后,LoginModules將相關(guān)principals和credentials與表示正在驗證的主題的Subject對象關(guān)聯(lián)。 -
LoginContext將身份驗證狀態(tài)返回給應(yīng)用程序 - 如果身份驗證成功,應(yīng)用程序?qū)?code>LoginContext獲取主題。
LoginContext
javax.security.auth.login.LoginContext類提供了用于對subject進行身份驗證的基本方法,并提供了一種獨立于底層身份驗證技術(shù)開發(fā)應(yīng)用程序的方法。LoginContext查詢配置,以確定為特定應(yīng)用程序配置的身份驗證服務(wù)(或LoginModule)。因此,可以在應(yīng)用程序下插入不同的LoginModules,而不需要對應(yīng)用程序本身進行任何修改。
LoginContext提供了四個構(gòu)造函數(shù)可供選擇:
public LoginContext(String name) throws LoginException;
public LoginContext(String name, Subject subject) throws LoginException;
public LoginContext(String name, CallbackHandler callbackHandler)
throws LoginException
public LoginContext(String name, Subject subject,
CallbackHandler callbackHandler) throws LoginException
所有構(gòu)造函數(shù)都共享一個公共參數(shù):name。LoginContext使用這個參數(shù)作為登錄配置的索引,以確定為實例化LoginContext的應(yīng)用程序配置了哪些LoginModule
不接受Subject作為輸入?yún)?shù)的構(gòu)造函數(shù)實例化一個新Subject。
實際身份驗證通過調(diào)用以下方法進行:
public void login() throws LoginException;
當(dāng)調(diào)用login時,將調(diào)用所有配置的loginmodule來執(zhí)行身份驗證。如果認(rèn)證成功,可以使用以下方法檢索Subject(現(xiàn)在可以保存主體、公共憑證和私有憑證):
public Subject getSubject();
要注銷Subject并刪除其經(jīng)過身份驗證的主體和憑據(jù),提供以下方法:
public void logout() throws LoginException;
下面的代碼示例演示了驗證和注銷主題所需的調(diào)用:
// let the LoginContext instantiate a new Subject
LoginContext lc = new LoginContext("entryFoo");
try {
// authenticate the Subject
lc.login();
System.out.println("authentication successful");
// get the authenticated Subject
Subject subject = lc.getSubject();
...
// all finished -- logout
lc.logout();
} catch (LoginException le) {
System.err.println("authentication unsuccessful: " +
le.getMessage());
}
LoginModule
LoginModule接口使開發(fā)人員能夠?qū)崿F(xiàn)可以在應(yīng)用程序中插入的各種身份驗證技術(shù)。例如,一種類型的LoginModule可以執(zhí)行基于用戶名/密碼的身份驗證形式。其他LoginModule可以與硬件設(shè)備(如智能卡或生物識別設(shè)備)來進行認(rèn)證
CallbackHandler
在某些情況下,LoginModule必須與用戶通信才能獲得身份驗證信息javax.security.auth.callback.CallbackHandler就是用于此目的。應(yīng)用程序?qū)崿F(xiàn)CallbackHandler接口并將其傳遞給LoginContext,后者將其直接轉(zhuǎn)發(fā)給底層LoginModule
LoginModule使用CallbackHandler收集用戶的輸入(如密碼或智能卡密碼)或向用戶提供信息(如狀態(tài)信息),通過允許應(yīng)用程序指定CallbackHandler,底層LoginModule可以實現(xiàn)不同的應(yīng)用程序與用戶交互方式。例如,GUI應(yīng)用程序的CallbackHandler實現(xiàn)可能會顯示一個窗口來請求用戶輸入。另一方面,非gui工具的CallbackHandler實現(xiàn)可能只是直接從命令行提示用戶輸入。
CallbackHandler是一個接口,有一個方法可以實現(xiàn):
void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException;
LoginModule向CallbackHandler 的handle方法傳遞一系列適當(dāng)?shù)幕卣{(diào)函數(shù),例如用戶名的NameCallback和密碼的PasswordCallback, CallbackHandler執(zhí)行請求的用戶交互并在回調(diào)函數(shù)中設(shè)置適當(dāng)?shù)闹?。例如,要處?code>NameCallback, CallbackHandler可能會提示輸入名稱,然后獲取輸入name,并調(diào)用NameCallback的setName方法來存儲name。
Callback
javax.security.auth.callback包包含回調(diào)接口和幾個實現(xiàn)。LoginModules可以將一個回調(diào)數(shù)組直接傳遞給CallbackHandler的handle方法。