Java EE Security API

Java™ EE Security API 1.0 規格 定義一組安全相關註釋,可用來進行鑑別。 此規格還定義一組共通的 API,可讓您用於程式化安全。

Java EE Security API 1.0 規格將自行包含的應用程式安全功能延伸至不同應用程式伺服器之間,並使用現代程式設計概念,例如「表示式語言 (EL)」和「環境定義和相依關係注入 (CDI)」。 下列清單包含 Liberty 設定檔特定的資訊,以支援規格。

appSecurity-3.0

appSecurity-3.0 特性提供 Java EE Security API 1.0 規格 (JSR 375 規格) 的核心功能。 此特性包含 el-3.0cdi-2.0 特性。 如果您想配置 DatabaseIdentityStore 註釋,請配置其中一項 jdbc-4.x 特性。 javaee-8.0webProfile-8.0 特性包含所有必要的特性。

appSecurity-3.0 特性支援 Liberty 設定檔中的規格。 此特性支援 SecurityContext API,以及規格中所定義的所有註釋。 為了驗證您的認證,並收集群組資訊,此規格支援使用 BASIC、FORM 和 CustomFORM 等之類註釋的鑑別機制,以及支援「輕量型目錄存取通訊協定 (LDAP)」和資料庫身分儲存庫。 appSecurity-3.0 特性也會在 SecurityContext 類別中指定一組共通的 API,來執行程式化安全檢查。 Liberty 設定檔為規格中的所有註釋提供 CDI Bean 實作。 此外,應用程式可針對這些註釋,提供自己的自訂 CDI Bean 實作。

LDAPIdentityStore

您可以使用 Liberty 設定檔中的密碼公用程式功能,來加密或編碼 bindDNPassword 屬性的密碼。 如需使用 securityUtility 指令來加密密碼的相關資訊,請參閱 securityUtility 指令

DataBaseIdentityStore

資料庫身分儲存庫需要一項資料來源配置,以提供資料庫資訊。 DatabaseIdentityStore 註釋上的 dataSourceLookup 元素會參照 Liberty 配置中 (例如 server.xml 檔) 或 Datasource 註釋中所定義的資料來源。 此外,在 Liberty 配置中定義資料庫類型的 JDBC 驅動程式庫。

您可以透過多種方式配置資料來源。 如需在資料來源上定義鑑別別名的相關資訊,請參閱 配置 Liberty 的鑑別別名。 如果資料來源使用鑑別別名,來定義資料來源的使用者和密碼,則 DatabaseIdentityStore 註釋上的 datasourceLookup 參數必須是資料來源的間接 JNDI 查閱,才能尋找其他資源。 例如, DatabaseIdentityStore 註釋上 datasourceLookup 參數所定義的間接 JNDI 查閱會是 java:comp/env/jdbc/MyDatasource

授權

根據您的需求,選擇如何設定您應用程式的授權。

  • 配置授權,以使用自動化角色來將對映分組:
    • 請勿在 ibm-application-bnd.xmiibm-application-bnd.xmlserver.xml 檔的 application-bnd 元素中配置任何權限表或角色連結。
    • 請確定身分儲存庫傳回的群組名稱,與角色名稱相同。
      • 當您使用 LDAPIdentityStore 註釋時,群組名稱會對應至註釋的 groupNameAttribute 屬性。 例如,假設存在下列的 LDAP 群組物件:
        cn=group1, o=mycompany, c=us
        當 groupNameAttribute 屬性的值設為 cn時,LdapIdnetityStore 註釋會傳回 group1 作為群組名稱。 因此,角色名稱必須配置為 group1
  • 指定一組使用者或群組,以授與權限:
    • 在該應用程式的 application-bnd 項目中,配置角色連結。
    • 視伺服器中的身分儲存庫配置而定,您可以採取不同的配置方式。
      當您使用 LDAPIdentityStore 註釋時,群組名稱會對應至註釋的 groupNameAttribute 屬性。 這個名稱必須對應至安全角色中之群組屬性的存取 ID。 例如,假設存在下列的群組物件:
      cn=group1, o=mycompany, c=us
      當 groupNameAttribute 屬性值設為 dn時, ldapIdnetityStore 註釋會傳回 cn=group1, o=mycompany, c=us 作為群組名稱。 因此,必須將 application-bnd 元素中的 access-id 設為 cn=group1, o=myCompany, c=us

請參閱下列範例,以便採不同方式來配置角色連結:

  • 在下列範例中,需要 access-idname 欄位。 access-id 欄位的完整格式是 user:realmName/userUniqueID(若為使用者)和 group:realmName/groupUniqueID(若為群組)。 使用者和群組的 uniqueIDs 會對應至應用程式身分提供者所傳回的資訊。

    例如,當您使用 LDAPIdentityStore 註釋時,userUniqueID 是使用者的識別名稱 (DN),groupUniqueID 則是以 LDAPIdentityStore 註釋中的 groupNameAttribute 設定為基礎。 這些值也會對應至驗證方法身分儲存庫在 CredentialValidationResult 物件中所傳回的 getCallerUniqueIDgetCallerGroups 方法。 realmName 會對應至所用身分儲存庫的 IdentityStoreID

  • 如果應用程式只使用一個身分儲存庫,您可以配置 application-bnd 項目,如下列範例所示。 由於只配置一個身分儲存庫,因此不需提供 realmNameIdentityStoreID
    <application type="war" id="sample" name="sample" location="sample.war">
        <application-bnd>
                    <security-role name="role1">
                       <user name="bob" access-id="cn=Bob Smith, o=myCompany, c=us" />                                      
                       <group name="group1" access-id="cn=group1, o=myCompany, c=us" />
                    </security-role>          
        </application-bnd>
    </application>
  • 如果應用程式使用多個身分儲存庫,請配置使用者和群組所在的對應 realmNameIdentityStoreID

    在下列範例中,ldapHostName:636 是 LDAP 身分儲存庫傳回的 IdentityStoreID。 當您使用資料庫身分儲存庫時,IdentityStoreID 會是用於資料庫的 dataSourceLookup 名稱,這是資料來源的完整 JNDI 名稱。 realmNameIdentityStoreID 用來在不同的身分儲存庫之間識別使用者和群組。 當您使用自訂身分儲存庫時,值必須符合對應的身分儲存庫 ID。

    • LDAP 身分儲存庫範例:
      <application type="war" id="sample" name="sample" location="sample.war">
          <application-bnd>
                      <security-role name="role1">
                         <user name="bob" access-id="ldapHostName:636/cn=Bob Smith, o=myCompany, c=us" />                                      
                         <group name="group1" access-id="ldapHostName:636/cn=group1,o=myCompany,c=us" />
                      </security-role>          
          </application-bnd>
      </application>
    • 資料庫身分儲存庫範例:
      <application type="war" id="sample" name="sample" location="sample.war">
          <application-bnd>
                      <security-role name="role1">
                         <user name="bob" access-id="jdbc/myDataSource/fullNameofBob" />                                      
                         <group name="group1" access-id="jdbc/myDataSource/fullGroupNameofGroup1" />
                      </security-role>          
          </application-bnd>
      </application>
      
  • 此外,伺服器支援現行延伸格式,可以使用 realmNameuniqueID 資訊,來指定使用者和群組的 access-id。 請使用延伸格式,來明確配置完整的 access-id 檔。
    • LDAP 身分儲存庫範例:
      <application type="war" id="sample" name="sample" location="sample.war">
          <application-bnd>
                      <security-role name="role1">
                         <user name="bob" access-id="user:ldapHostName:636/cn=Bob Smith, o=myCompany, c=us" />                                      
                         <group name="group1" access-id="group:ldapHostName:636/cn=group1, o=myCompany, c=us" />
                      </security-role>          
          </application-bnd>
      </application>
      
    • 資料庫身分儲存庫範例:
      
      <application type="war" id="sample" name="sample" location="sample.war">
              <application-bnd>
                      <security-role name="role1">
                            <user name="bob" access-id="user:jdbc/myDataSource/fullNameofBob" />                                      
                            <group name="group1" access-id="group:jdbc/myDataSource/fullGroupNameofGroup1" />
                      </security-role>          
            </application-bnd>
      </application>

EAR 檔

當您在 EAR 檔中配置應用程式的安全註釋時,請考量下列各點:
  • 若有 EJB 模組是應用程式的一部分,請將 server.xml 檔中的使用者登錄配置用於該應用程式中的所有模組,而非使用身分儲存庫。
  • 如果需要配置 Web 模組的身分儲存庫,請將相同的身分儲存庫用於所有的 Web 模組。 如果 EAR 檔也包含 EJB 模組,則 server.xml 檔中所配置的使用者登錄必須符合所配置的身分儲存庫。 必須符合的資訊包括:使用者和群組,以及身分儲存庫 ID 之使用者登錄的網域範圍名稱。
  • 如果這些模組會彼此通訊,當發生下列其中一種狀況時,請配置授權區段中所指定的完整存取 ID:
    • 應用程式中的模組包含不同的身分儲存庫。
    • server.xml 檔中的使用者登錄與身分儲存庫不符。
    視應用程式中之模組之間所需的通訊而定,這項配置可能使配置趨於複雜。

置換應用程式所提供的鑑別方法或機制。

您可以配置伺服器層次指定的鑑別機制,來置換應用程式使用的任何鑑別機制。
  • 將這個置換動作配置成使用基本鑑別機制。
    <webAppSecurity overrideHttpAuthMethod="BASIC" basicAuthenticationMechanismRealmName="myRealm" />
    
    如果沒有指定 basicAuthenticationMechanismRealmName 屬性,則會使用 defaultRealm 值。
  • 將這個置換動作配置成使用表單鑑別機制。
    請同時指定登入表單頁面和登入錯誤頁面。 這兩個頁面必須是個別 WAR 檔的一部分。
    • 利用 contextRootForFormAuthenticationMechanism 元素,來指定登入頁面的環境定義根目錄。
      <webAppSecurity loginFormURL="/global/login/globalLogin.jsp" loginErrorURL="/global/login/globalLoginError.jsp"
           overrideHttpAuthMethod="FORM" contextRootForFormAuthenticationMechanism="/global/login" />
      
          <application type="war" context-root="/global/login" id="globalLogin" name="globalLogin" location="globalLogin.war"/>
  • 將這個置換動作配置成使用用戶端憑證。
    您可以將 overideHttpAuthMethod 方法設為 CLIENT_CERT,以使用用戶端憑證來鑑別所有應用程式:
    <webAppSecurity overrideHttpAuthMethod="CLIENT_CERT"/>
    如果用戶端憑證鑑別失敗,您可以指定 allowAuthenticationFailOverToAuthMethod 元素,以配置成由不同的鑑別方法失效接手。 allowAuthenticationFailOverToAuthMethod 元素可接受的值如下:
    • BASIC:使用基本鑑別。
      下列範例顯示由基本登入失效接手:
      <webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="BASIC"/>
    • FORM:使用表單登入。 當值設為 FORM 時,請確定已設定 loginFormURL 和 loginErrorURL 屬性。

      下列範例顯示由表單登入失效接手:

      指定 logindForm、loginError URL,以及一個包裝了那些檔案的 .war 檔:
      <webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="FORM" loginFormURL="globalLogin/globalLogin.jsp" loginErrorURL="globalLogin/globalLoginError.jsp"/>
          <application type="war" id="globalLogin" name="globalLogin" location="globalLogin.war"/>  
    • APP_DEFINED:使用的登入方法是 web.xml 檔或註釋中配置給應用程式的方法。

      下列範例顯示由應用程式中以 APP_DEFINED 值所定義的鑑別方法失效接手:

       <webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="APP_DEFINED"/>

    當您使用用戶端鑑別時,請將 SSL 元素的 clientAuthentication 或 clientAuthenticationSupported 屬性設為 true,讓用戶端透過憑證來傳送。

實作自訂 HTTP 鑑別機制或身分儲存庫時要考量的一些事項。

當您使用 HttpAuthenticationMechanism 介面來實作自訂 HTTP 鑑別機制,或使用 IdentifyStore 介面來實作身分儲存庫時,請考量下列要點:
  • 請標註您的自訂 HttpAuthenticationMechanism 介面,使其使用您所實作的 RememberMeIdentityStore 介面。 藉由實作自訂 HttpAuthenticationMechanism 介面,您可以在環境中採行單一登入,如此一來,使用者後續每一次登入時,都可以使用 RememberMe Cookie 中的資訊,而不必每次都要登入。
  • 請勿同時使用 RememberMe Cookie 和 AutoApplySession 註釋,這是因為它們提供的用途類似,且兩者同時使用,會導致不一致的授權行為。
  • 如果您提供自己的 IdentityStore 實作,而在 CredentialValidationResult 類別中設定 ID,並且以 RememberMe 註釋來標註您的自訂 HttpAuthenticationMechanism 實作,請確定角色連結的 access-id 欄位中未設定網域範圍名稱。 您想確定網域範圍名稱不是以這種方式來設定,因為會以 IdentityStore ID 作為授權網域範圍,但是在處理 RememberMe Cookie 時,不會保留 IdentityStore ID。
  • 使用 IdentityStore ID 來建立 CredentialValidationResult 類別,以便以該 ID 作為網域範圍名稱。

實作資料庫身分儲存庫的自訂密碼雜湊

對於使用資料庫的身分儲存庫,系統提供了預設 PasswordHash 實作。 需要 PasswordHash,才能驗證資料庫中的使用者密碼。 如果要提供自訂 PasswordHash 實作,請實作 javax.security.enterprise.identitystore.PasswordHash,並以 CDI Bean 形式提供於共用程式庫中。

  1. 開發 javax.security.enterprise.identitystore.PasswordHash 實作。 下列範例顯示一個範例虛擬密碼雜湊:
    package com.ibm.ws.security.pwdhash.test;
    
    import javax.security.enterprise.identitystore.PasswordHash;
    
    /**
    * Test PasswordHash sample for DatabaseIdentityStore. For testing purposes only.
    */
    public class TestHash implements PasswordHash {
    
      public TestHash() {
        System.out.println("Init TestHash");
      }
    
      @Override
      public boolean verify(char[] incomingPwd, String existingPwd) {
        System.out.println("TestHash is for testing purposes only.");
        String pwd = String.valueOf(incomingPwd) + "_DUMMY_HASH";
        if (pwd.equals(existingPwd)) {
            return true;
        }
        return false;
      }
    
      @Override
      public String generate(char[] pwdToHash) {
        return String.valueOf(pwdToHash) + "_DUMMY_HASH";
      }
    }
  2. 將 PasswordHash 實作包裝在 JAR 中,並以 CDI Bean 形式提供 PasswordHash 類別。 您可以利用不同的作法,將此類別標示為 Bean。
    1. 其中一個選項是將含有 bean-discovery-mode=allbeans.xml 檔案新增至 jar 的 META-INF 資料夾。 以下是beans.xml 檔範例
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_2.xsd"
             bean-discovery-mode="all" version="1.1">
      </beans>
    2. 另一個選項是將 @dependent 註釋新增至 PasswordHash 類別
      package com.ibm.ws.security.pwdhash.test;
      
      import javax.enterprise.context.Dependent;
      import javax.security.enterprise.identitystore.PasswordHash;
      
      /**
      * Test PasswordHash sample for DatabaseIdentityStore. For testing purposes only.
      */
      @Dependent
      public class TestHash implements PasswordHash {
      ...
  3. library 元素形式將 PasswordHash 實作新增至 server.xml,其中 dir 屬性指向 JAR 檔的位置。 下列範例顯示新增 library 元素
    <library id="CustomHashLib" fat.modify="true">
        <fileset dir="${server.config.dir}" includes="CustomPasswordHash.jar"/>
    </library>
  4. commonLibraryRef 參照新增至 server.xml 中的資料庫 IdentityStore 應用程式。 下列範例顯示新增 classloader 元素至應用程式。
     <application type="war" id="DatabaseAnnotatedCustomHashServlet" name="DatabaseAnnotatedCustomHashServlet" location="DatabaseAnnotatedCustomHash.war">
      <application-bnd>
        <security-role name="javaeesec_basic">
          <user name="blue1" access-id="user:java:comp/DefaultDataSource/blue1" />
       </application-bnd>
       ...
       <classloader commonLibraryRef="CustomHashLib" />
    </application>
  5. DatabaseIdentityStoreDefinition 註釋上的 hashAlgorithm 內容應符合 JAR 中提供的類別。 以下是 DatabaseIdentityStoreDefinition 註釋的範例
    @DatabaseIdentityStoreDefinition( 
    callerQuery = "select password from callers where name = ?", 
    groupsQuery = "select group_name from caller_groups where caller_name = ?", 
    hashAlgorithm = com.ibm.ws.security.pwdhash.test.TestHash.class)
  6. 使用相同的自訂雜湊演算法,來產生密碼並儲存在資料庫 IdentityStore 所存取的資料庫中。

使用預設密碼雜湊提供者來加密資料庫身分儲存庫的密碼

如果您想在 DatabaseIdentityStores 介面中取得預設 passwordHash 實作,以便處理使用者密碼的加密和解密,您可以使用 CDI:
Instance<? extends PasswordHash> instance = CDI.current().select(Pbkdf2PasswordHash.class);
PasswordHash passwordHash = instance.get();
passwordHash.initialize(..)
password.generate(...)

將 LTPA 記號用於「單一登入 (SSO)」

Java EE Security API 1.0 (JSR 375) 支援 RememberMeIdentityStore 介面。 JSR 375 提供者可以實作這個介面,這樣一來,在起始鑑別之後,就會記住使用者資訊。 RememberMeIdentityStore 實作產生的 Cookie 可用來達成 SSO,因而在到期之前,不必再次提示使用者提供。 此功能提供更大的彈性讓提供者達成 SSO,當您的應用程式中配置了不同的身分儲存庫時,需要此功能。

除了定義 RememberMeIdentityStore 介面的規格之外,您也可以使用 Liberty 設定檔所產生 LTPA Cookie 中的 LTPA 記號來達到 SSO。 當您在 JSR 375 應用程式中使用 LTPA Cookie 時,請注意下列資訊。

  • 在您使用 JSR 375 規格時,會將其他資訊新增至 LTPA Cookie 中的 LTPA 記號。 當重新傳回此 LTPA 記號,或是傳送給相同 SSO 網域中的另一部伺服器時,就會驗證它。 如果驗證成功,就會呼叫 HttpAuthenticationMechanism 實例,或是呼叫 JASPIC 提供者(如果改配置此提供者的話),以完成鑑別。
  • 如果只在啟用了 JSR 375 的應用程式之間使用 SSO,並且只使用預設 HttpAuthMechanism 實作,就會驗證 LTPA 記號。 如果驗證成功,就不會再次提示使用者登入。 驗證記號時,需要交換 LTPA 金鑰且使用者登錄必須相同,或者必須在所有應用程式中配置相同的身分儲存庫。 如果驗證失敗,則會根據應用程式中配置的 HttpAuthMechanism 實作,提示使用者登入。 如需以記號為基礎之 LTPA 記號型 SSO 的相關資訊,請參閱 在 Liberty 中使用 LTPA Cookie 自訂 SSO 配置
  • 如果出示給已啟用 JSR 375 之應用程式的 LTPA Cookie,是來自非 JSR 375 應用程式,即使在驗證記號之後,還是會呼叫 HttpAuthenticationMechanism 實作。 會進行這項呼叫,乃根據規格需求。 即使配置了 JASPIC 提供者,仍然會有這項需求。 之後 HttpAuthMechanism 實作會再次提示使用者提供。
  • 如先前的清單項目所述,可能有人不想收到提示。 例如,您可能希望使用已驗證過的 LTPA 記號來鑑別使用者,而不要呼叫 HttpAuthenticationMechanism 實作或 JASPIC 提供者。 在此情況下,請在 server.xml 檔中設定下列內容:
    <webAppSecurity useLtpaSSOForJaspic="true" />
  • 當設定了這個內容,並出示有效的 LTPA 記號時,就會使用該記號來鑑別使用者,而不會呼叫 HttpAuthenticationMechanism 實作或 JASPIC 提供者。 使用者登錄在參與 SSO 的所有伺服器之間必須相同,才能將這個旗標設為 true。 如果在程式碼中指定了任何身分儲存庫,請勿設定此旗標,因為在所有伺服器中的所有應用程式中,identitystores 實作不見得都相同。 會設定此旗標的典型實務是,只有在程式碼中指定了 HttpAuthMechanism 註釋,且未指定任何身分儲存庫。 在此情況下,會使用 server.xml 檔中所配置的使用者登錄。
  • 如果您希望將特定的 LTPA Cookie 用於某部伺服器,可以設定下列 ssoCookieName 屬性,以便為每一部伺服器設定特定的自訂 LTPA Cookie名稱。 在此情況下,該伺服器只會產生並驗證所配置的特定 LTPA Cookie。 不會使用其他伺服器所產生的其他任何 LTPA Cookie。
    <webAppSecurity useOnlyCustomCookieName="true", ssoCookieName="myServerXCookieName" />  ------> Change the cookieName and make it unique across all the servers
  • 如果將 JSR 375 伺服器或 JASPIC 伺服器產生的 LTPA Cookie,出示給非 JSR 375 伺服器或非 JASPIC 伺服器,就會進行驗證並用於 SSO,而不會提示使用者提供。