JAAS 參考指南

術(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對象又引用ConfigurationLoginModule,用于執(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)建了一個principalcredential為空(非空)的subject。第二個構(gòu)造函數(shù)使用指定的principalscredentials創(chuàng)建subject。它還有一個布爾參數(shù),可用于使subject為只讀。在只讀subject中,principalscredentials是不可變的。

應(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);

要修改或操作subjectprincipals、pubCredentialsprivCredentials,調(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)限。

如果提供給doasprivilegeAccessControlContextnull,則操作不受單獨的AccessControlContext的限制。比如在服務(wù)器環(huán)境中。服務(wù)器可以對多個傳入請求進行身份驗證,并為每個請求執(zhí)行單獨的doAs操作。要啟動每個doAs操作,并且不受當(dāng)前服務(wù)器AccessControlContext的限制,服務(wù)器可以調(diào)用doAsPrivileged并傳入nullAccessControlContext。

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.Principaljava.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)的接口:RefreshableDestroyable

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í)行以下步驟:

  1. 應(yīng)用程序?qū)嵗?code>LoginContext
  2. LoginContext查詢一個Configuration,以加載為該應(yīng)用程序配置的所有LoginModules 。
  3. 應(yīng)用程序調(diào)用LoginContextlogin 方法
  4. login方法調(diào)用所有加載的LoginModules 。每個LoginModule都嘗試對subject進行身份驗證,成功后,LoginModules將相關(guān)principalscredentials與表示正在驗證的主題的Subject對象關(guān)聯(lián)。
  5. LoginContext將身份驗證狀態(tài)返回給應(yīng)用程序
  6. 如果身份驗證成功,應(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;

LoginModuleCallbackHandlerhandle方法傳遞一系列適當(dāng)?shù)幕卣{(diào)函數(shù),例如用戶名的NameCallback和密碼的PasswordCallback, CallbackHandler執(zhí)行請求的用戶交互并在回調(diào)函數(shù)中設(shè)置適當(dāng)?shù)闹?。例如,要處?code>NameCallback, CallbackHandler可能會提示輸入名稱,然后獲取輸入name,并調(diào)用NameCallbacksetName方法來存儲name。

Callback

javax.security.auth.callback包包含回調(diào)接口和幾個實現(xiàn)。LoginModules可以將一個回調(diào)數(shù)組直接傳遞給CallbackHandlerhandle方法。

Authorization 類
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容