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

Authorization

请根据您的需求选择如何设置应用程序授权。

  • 配置授权以使用自动角色到组映射:
    • 请勿在 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 属性。 此名称必须与安全角色中组属性的访问标识相对应。 例如,假定存在以下组对象:
      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 设置。 这些值还对应于 validate 方法身份库所返回的 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 用于区分不同身份库中的用户和组。 使用定制身份库时,应使这些值与对应身份库标识匹配。

    • 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 文件中配置的用户注册表必须与配置的身份库匹配。 必须匹配的信息包括用户和组,以及用户注册表的领域名称与身份库标识匹配。
  • 如果模块相互通信,那么符合下列其中一个条件时,请配置授权部分中指定的完整访问标识:
    • 该应用程序中的模块包含不同的身份库。
    • server.xml 文件中的用户注册表与身份存储库不匹配。
    根据应用程序中的模块之间所需的通信,这可能会导致复杂的配置。

覆盖应用程序所提供的认证方法或机制。

您可以配置在服务器级别指定的认证机制,以覆盖应用程序所使用的任何认证机制。 认证机制仅适用于用户应用程序。 更多信息,请参阅webAppSecurity元素。
  • 配置此覆盖以使用基本认证机制。
    <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 属性。

      进行故障转移以切换到表单登录的示例如下:

      请指定 loginForm 和 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 注释,因为它们具有类似的用途,同时使用这两个 Cookie 会导致不一致的授权行为。
  • 如果您已提供自己的 IdentityStore 实现来在 CredentialValidationResult 类中设置标识,并已使用 RememberMe 注释对定制 HttpAuthenticationMechanism 实现添加注释,请确保没有在角色绑定的 access-id 字段中设置领域名称。 您希望确保没有以此方式设置领域名称,因为 IdentityStore 标识用作授权领域,但是处理 RememberMe Cookie 时,不会保留 IdentityStore 标识。
  • 创建具有 IdentityStore 标识的 CredentialValidationResult 类,以便可以将该标识用作领域名称。

实现用于数据库身份库的定制密码散列

系统为使用数据库的身份库提供了缺省 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. 将 PasswordHash 实现 JAR 作为 library 元素添加到 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 访问。

使用缺省密码散列提供者对数据库身份库用户的密码进行加密

如果要获取缺省 passwordHash 实现(用于处理 DatabaseIdentityStores 接口中用户密码的加密和解密),那么可以使用 CDI:
Instance<? extends PasswordHash> instance = CDI.current().select(Pbkdf2PasswordHash.class);
PasswordHash passwordHash = instance.get();
passwordHash.initialize(..)
password.generate(...)

对单点登录 (SSO) 使用 LTPA 令牌

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 提供者(如果改为配置此项)以完成认证。
  • 如果 SSO 在已启用 JSR 375 的应用程序中是唯一的,并且仅使用缺省 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 服务器,那么系统会验证该 cookie 并将其用于 SSO 而不提示用户。