Java EE Security API

La especificación Java™ EE Security API 1.0 define un conjunto de anotaciones relacionadas con la seguridad que puede utilizar para la autenticación. La especificación también define un conjunto común de API que puede utilizar para una seguridad programática.

La especificación Java EE Security API 1.0 amplía las prestaciones de seguridad de aplicaciones autocontenidas para portar entre distintos servidores de aplicaciones y utiliza conceptos de programación modernos como, por ejemplo, Expression Language (EL) y Contexts and Dependency Injection (CDI). La siguiente lista contiene información específica de Liberty para apoyar la especificación.

appSecurity-3.0

La característica appSecurity-3.0 proporciona la prestación básica para la especificación Java EE Security API 1.0 (especificación JSR 375). Esta característica incluye las características el-3.0 y cdi-2.0. Si desea configurar la anotación DatabaseIdentityStore, configure una de las características jdbc-4.x. Las características javaee-8.0 y webProfile-8.0 incluyen todas las características necesarias.

La función " appSecurity-3.0 " admite la especificación en Liberty. Esta característica soporta la API SecurityContext y todas las anotaciones definidas en la especificación. Para validar las credenciales y recopila información de grupos, la especificación incluye soporte para mecanismos de autenticación que usan anotaciones como, por ejemplo, BASIC, FORM y CustomFORM, así como Lightweight Directory Access Protocol (LDAP) y almacenes de identidad en base de datos. La característica appSecurity-3.0 también especifica un conjunto común de las API en la clase SecurityContext para realizar comprobaciones de seguridad programáticas. El perfil Liberty proporciona implementaciones de bean CDI para todas las anotaciones de la especificación. Además, las aplicaciones pueden proporcionar sus propias implementaciones de bean CDI personalizadas para dichas anotaciones.

LDAPIdentityStore

Puedes utilizar las utilidades de contraseña de Liberty para encriptar o codificar la contraseña del atributo ' bindDNPassword '. Para obtener más información sobre el cifrado de contraseñas con el mandato securityUtility , consulte MandatosecurityUtility.

DataBaseIdentityStore

El almacén de identidades en base de datos requiere una configuración de origen de datos para proporcionar la información de base de datos. El elemento dataSourceLookup de la anotación DatabaseIdentityStore hace referencia al origen de datos definido en la configuración de Liberty , como el archivo server.xml , o en una anotación Datasource . Además, defina la biblioteca del controlador JDBC para el tipo de base de datos en la configuración de Liberty .

Puede configurar un origen de datos de varias maneras. Para obtener más información sobre cómo definir un alias de autenticación en el origen de datos, consulte Configuración de alias de autenticación para Liberty. Si el origen de datos utiliza un alias de autenticación para definir el usuario y la contraseña para el origen de datos, el parámetro datasourceLookup en la anotación DatabaseIdentityStore debe ser una consulta JNDI indirecta del origen de datos para encontrar los recursos adicionales. Por ejemplo, la búsqueda JNDI indirecta que se define en el parámetro datasourceLookup de la anotación DatabaseIdentityStore sería ' java:comp/env/jdbc/MyDatasource.

Autorización

Elija cómo configurar la autorización para su aplicación en función de sus requisitos.

  • Configure la autorización para utilizar la correlación automática de roles con grupos:
    • No configure ninguna tabla de autorización ni enlaces de rol en el elemento application-bnd en los archivos ibm-application-bnd.xmi, ibm-application-bnd.xml o server.xml.
    • Asegúrese de que el nombre de grupo devuelto por el almacén de identidades sea el mismo que el nombre de rol.
      • Cuando se utiliza la anotación LDAPIdentityStore, el nombre del grupo corresponde al atributo groupNameAttribute de la anotación. Por ejemplo, suponga que existe el siguiente objeto de grupo LDAP:
        cn=group1, o=mycompany, c=us
        La anotación LdapIdnetityStore devuelve group1 como nombre de grupo cuando el valor del atributo groupNameAttribute se establece en cn. Por lo tanto, el nombre de rol debe configurarse como group1.
  • Especifique un conjunto de usuarios o grupos para la autoridad de autorización:
    • Configure los enlaces de rol en la entrada application-bnd para dicha aplicación.
    • Se puede configurar de diferentes maneras en función de las configuraciones de almacén de identidades del servidor.
      Cuando se utiliza la anotación LDAPIdentityStore, el nombre del grupo corresponde al atributo groupNameAttribute de la anotación. Este nombre debe corresponder al ID de acceso del atributo de grupo en el rol de seguridad. Por ejemplo, suponga que el siguiente objeto de grupo existe:
      cn=group1, o=mycompany, c=us
      La anotación LdapIdnetityStore devuelve cn=group1, o=mycompany, c=us como nombre de grupo cuando el valor del atributo groupNameAttribute se establece en dn. Por lo tanto, el access-id en el elemento application-bnd debe establecerse en cn=group1, o=myCompany, c=us.

Consulte los ejemplos siguientes para configurar enlaces de rol de diferentes formas:

  • En los ejemplos siguientes, los campos access-id y name son necesarios. El formato completo del campo access-id es user:realmName/userUniqueID para usuarios y group:realmName/groupUniqueID para grupos. Los uniqueIDs de usuario y grupo corresponden a la información que devuelven los proveedores de identidad de aplicación.

    Por ejemplo, cuando se utiliza la anotación LDAPIdentityStore, userUniqueID es el nombre distinguido (DN) para el usuario y groupUniqueID se basa en el valor groupNameAttribute de la anotación LDAPIdentityStore. Estos valores también se corresponden con los métodos getCallerUniqueID y getCallerGroups en el objeto CredentialValidationResult devuelto por el almacén de identidades de método de validación. realmName corresponde al IdentityStoreID del almacén de identidades que se utiliza.

  • Si una aplicación utiliza sólo un almacén de identidades, puede configurar la entrada application-bnd como se muestra en el ejemplo siguiente. No es necesario proporcionar realmName o IdentityStoreID porque sólo se configura un almacén de identidades:
    <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>
  • Si una aplicación utiliza varios almacenes de identidades, configure los correspondientes realmName o IdentityStoreID donde existan el usuario y el grupo.

    En el ejemplo siguiente, ldapHostName:636 es el IdentityStoreID devuelto por el almacén de identidades LDAP. Cuando se utiliza el almacén de identidades de base de datos, IdentityStoreID es el nombre dataSourceLookup que se utiliza en la base de datos, que es el nombre JNDI completo del origen de datos. realmName y IdentityStoreID se usan para distinguir usuarios y grupos entre distintos almacenes de identidades. Cuando use sus propios almacenes de identidades personalizados, haga que los valores coincidan con los correspondientes identificadores del almacén de identidades.

    • Ejemplo de almacén de identidades 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>
    • Ejemplo de almacén de identidades de base de datos:
      <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>
      
  • Además, el servidor soporta el formato ampliado actual para especificar access-id para el usuario y el grupo con la información de realmName y uniqueID. Utilice el formato ampliado para configurar explícitamente el archivo access-id completo.
    • Ejemplo de almacén de identidades 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>
      
    • Ejemplo de almacén de identidades de base de datos:
      
      <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 file

Cuando configure las anotaciones de seguridad para aplicaciones en un archivo EAR, tengo en cuenta los siguientes puntos:
  • Si algún módulo EJB forma parte de la aplicación, utilice la configuración del registro de usuarios en el archivo server.xml para todos los módulos de dicha aplicación en lugar de utilizar almacenes de identidad.
  • Si es necesario configurar almacenes de identidad para los módulos web, utilice los mismos almacenes de identidad para todos los módulos web. Si el archivo EAR también contiene módulos EJB, el registro de usuarios que está configurado en el archivo server.xml debe coincidir con los almacenes de identidad configurados. La información que debe coincidir incluye el usuario y grupo, y el nombre de reino del registro de usuarios para el ID de almacén de identidades.
  • Si los módulos se comunican entre sí, configure los ID de acceso completo tal como se especifica en la sección de autorización cuando se da una de estas condiciones:
    • Los módulos de la aplicación contienen almacenes de identidad diferentes.
    • El registro de usuarios del archivo server.xml no coincide con los almacenes de identidad.
    Esta configuración puede llevar a configuraciones complejas basadas en la comunicación que se necesita entre los módulos de la aplicación.

Altere temporalmente el mecanismo o método de autenticación proporcionado por la aplicación.

Puede configurar el mecanismo de autenticación que se especifica a nivel de servidor para alterar temporalmente cualquier mecanismo de autenticación que utiliza una aplicación. El mecanismo de autenticación sólo se aplica a las aplicaciones de usuario. Para más información, consulte el elemento webAppSecurity.
  • Configure la alteración temporal para utilizar el mecanismo de autenticación básica.
    <webAppSecurity overrideHttpAuthMethod="BASIC" basicAuthenticationMechanismRealmName="myRealm" />
    
    Si no se especifica el atributo basicAuthenticationMechanismRealmName, se utiliza el valor de defaultRealm.
  • Configure la alteración temporal para utilizar el mecanismo de autenticación de formulario.
    Especifique la página de formulario de inicio de sesión y la página de error de inicio de sesión. Estas dos páginas deben formar parte de un archivo WAR independiente.
    • Especifique la raíz de contexto para las páginas de inicio de sesión utilizando el elemento 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"/>
  • Configure la alteración temporal para que utilice el certificado de cliente.
    Puede utilizar un certificado de cliente para la autenticación de todas las aplicaciones estableciendo el método overideHttpAuthMethod en CLIENT_CERT:
    <webAppSecurity overrideHttpAuthMethod="CLIENT_CERT"/>
    Si la autenticación de certificado de cliente falla, puede configurar la migración tras error a un método de autenticación diferente especificando el elemento allowAuthenticationFailOverToAuthMethod. Los siguientes valores son aceptables para el elemento allowAuthenticationFailOverToAuthMethod:
    • BASIC: se utiliza la autenticación básica.
      Ejemplo de una migración tras error al inicio de sesión básico:
      <webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="BASIC"/>
    • FORM: se utiliza el inicio de sesión de formulario. Cuando el valor se establece en FORM, asegúrese de que se establecen los atributos loginFormURL y loginErrorURL.

      Ejemplo de una migración tras error al inicio de sesión de formulario:

      Especifique los URL loginForm y loginError y el archivo .war que empaquete estos archivos:
      <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: El método de inicio de sesión que se utiliza es el que se configura con la aplicación en el archivo web.xml o en la anotación.

      Ejemplo de una migración tras error para el método de autenticación definido en la aplicación utilizando el valor APP_DEFINED:

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

    Cuando utilice la autenticación de cliente, establezca el atributo clientAuthentication o clientAuthenticationSupported del elemento SSL en true para que el cliente envíe el certificado.

Cosas que se deben tener en cuenta al implementar un mecanismo de autenticación HTTP personalizado o un almacén de identidades.

Al implementar un mecanismo de autenticación HTTP personalizado con la interfaz HttpAuthenticationMechanism o un almacén de identidades con la interfaz IdentifyStore, considere los siguientes puntos:
  • Anote la interfaz HttpAuthenticationMechanism personalizada de modo que utilice la interfaz RememberMeIdentityStore que ha implementado. Al implementar la interfaz HttpAuthenticationMechanism personalizada, tendrá el inicio de sesión único para todo el entorno de modo que cada inicio de sesión subsiguiente para un usuario pueda utilizar la información en el cookie RememberMe y evitar la necesidad de iniciar la sesión cada vez.
  • No utilice el cookie RememberMe y la anotación AutoApplySession porque tienen objetivos parecidos y si se utilizan los dos puede ocasionar un comportamiento de autorización incoherente.
  • Si ha proporcionado su propia implementación IdentityStore que establece el ID en la clase CredentialValidationResult y ha anotado la implementación HttpAuthenticationMechanism personalizada con la anotación RememberMe, asegúrese de que el nombre de reino no se ha establecido en el campo access-id del enlace de rol. Desea asegurarse de que el nombre de reino no se ha establecido de esta forma porque el ID IdentityStore se utiliza como reino para la autorización, pero cuando se procesa el cookie RememberMe, el ID IdentityStore no se retiene.
  • Cree la clase CredentialValidationResult con el ID IdentityStore para que el ID pueda utilizarse como nombre de reino.

Implementación de un hash de contraseña personalizado para un almacén de identidades de base de datos

Se proporciona una implementación de PasswordHash predeterminada para un almacén de identidades utilizando una base de datos. PasswordHash es necesario para validar las contraseñas de usuario en la base de datos. Para proporcionar una implementación personalizada para PasswordHash, implemente javax.security.enterprise.identitystore.PasswordHash y hágalo disponible com un bean CDI en una biblioteca compartida.

  1. Desarrolle una implementación de javax.security.enterprise.identitystore.PasswordHash. El ejemplo siguiente muestra un hash de contraseña ficticio de ejemplo:
    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. Empaquete la implementación de PasswordHash en un jar y haga disponible la clase PasswordHash como un bean CDI. Hay varias maneras de marcar la clase como bean.
    1. Una opción es añadir un archivo beans.xml con bbean-discovery-mode=all a la carpeta META-INF del archivo jar. Lo siguiente es un ejemplo de archivo 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. Otra opción es añadir la anotación @dependiente a la clase 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. Añada el jar de implementación de PasswordHash como un elemento library a server.xml, donde el atributo dir apunta a la ubicación del archivo JAR. A continuación se muestra un ejemplo de la adición de un elemento library
    <library id="CustomHashLib" fat.modify="true">
        <fileset dir="${server.config.dir}" includes="CustomPasswordHash.jar"/>
    </library>
  4. Añada la referencia commonLibraryRef a la aplicación IdentityStore de la base de datos en server.xml. A continuación se muestra un ejemplo de la adición de un elemento classloader a una aplicación.
     <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. La propiedad hashAlgorithm en la anotación DatabaseIdentityStoreDefinition debe coincidir con la clase que se proporciona en el jar. A continuación se muestra un ejemplo de la anotación 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. Utilice el mismo algoritmo hash personalizado para generar contraseñas que están almacenadas en la base de datos a la que se accede mediante la base de datos IdentityStore.

Cifrado de la contraseña para usuarios del almacén de identidades de base de datos utilizando el proveedor hash de contraseña predeterminado

Si desea obtener la implementación passwordHash predeterminada que maneja el cifrado y el descifrado de las contraseñas de usuario en la interfaz DatabaseIdentityStores, puede utilizar CDI:
Instance<? extends PasswordHash> instance = CDI.current().select(Pbkdf2PasswordHash.class);
PasswordHash passwordHash = instance.get();
passwordHash.initialize(..)
password.generate(...)

Utilizar la señal LTPA para inicio de sesión único (SSO)

La Security API Java EE 1.0 (JSR 375) admite la interfaz RememberMeIdentityStore. Un proveedor JSR 375 puede implementar esta interfaz de manera que la información del usuario se recuerde después de la autenticación inicial. El cookie que genera la implementación RememberMeIdentityStore puede utilizarse para conseguir el inicio de sesión único sin tener que preguntar al usuario otra vez, hasta que caduque. Esta capacidad ofrece más flexibilidad al proveedor para realizar el inicio de sesión único y es obligatoria cuando tiene distintas entidades configuradas en las aplicaciones.

Además de la interfaz RememberMeIdentityStore definida en la especificación, también puede utilizar el token LTPA en la cookie LTPA que genera Liberty para lograr el SSO. Tenga en cuenta la siguiente información cuando utilice el cookie TPA en una aplicación JSR 375.

  • Cuando se utiliza la especificación JSR 375, se añade información adicional a la señal en el cookie LTPA. Cuando esta señal LTPA se devuelve de nuevo o se dirige a otro servidor del mismo dominio de SSO, se valida. Si la validación es satisfactoria se invoca la implementación HttpAuthenticationMechanism, o un proveedor JASPIC si así está configurado, para completar la autenticación.
  • Si el SSO se realiza únicamente entre aplicaciones JSR 375 habilitadas y solo se utilizan las implementaciones HttpAuthMechanism predeterminadas, la señal LTPA se valida. Si la validación resulta satisfactoria, no se vuelve a solicitar al usuario que inicie la sesión. La validación de la señal requiere que se intercambien las claves LTPA y que el registro de usuario sea el mismo, o que tenga configurados los mismos almacenes de identidades en todas las aplicaciones. Si la validación falla, se indica al usuario que inicie la sesión en base a la implementación HttpAuthMechanism configurada en la aplicación. Para obtener más información sobre SSO basado en señales LTPA que se basa en una señal, consulte Personalización de la configuración de SSO utilizando cookies LTPA en Liberty.
  • Si se presenta un cookie LTPA desde una aplicación que no es JSR 375 a una aplicación habilitada para JSR 375, se invoca la implementación HttpAuthenticationMechanism aunque se valide la señal. Esta llamada se realiza siguiendo los requisitos de la especificación. Este requisito también se cumple cuando se configura un proveedor JASPIC. Entonces, la implementación HttpAuthMechanism vuelve a interpelar al usuario.
  • Quizá no todos los usuarios deseen ser interpelados nuevo tal come se indica. Por ejemplo, podría utilizar la señal LTPA validada para autenticar al usuario sin invocar la implementación HttpAuthenticationMechanism ni un proveedor JASPIC. En esta situación, establezca la propiedad siguiente en el archivo server.xml:
    <webAppSecurity useLtpaSSOForJaspic="true" />
  • Cuando se establece esta propiedad y se presenta una señal LTPA válida, la señal se utiliza para autenticar al usuario sin invocar la implementación HttpAuthenticationMechanism ni el proveedor ASPIC. Establezca este distintivo en true solamente si el registro de usuarios es el mismo en todos los servidores que participan en el inicio de sesión único (SSO). Si hay algún almacén de identidades especificado en el código, no establezca el distintivo porque la implementación identitystores podría no ser la misma en todas las aplicaciones en todos los servidores. Un escenario común donde se establece este distintivo seria uno que solo especifica la anotación HttpAuthMechanism en el código, sin especificar ningún almacén de identidades. En este caso, se utiliza el registro de usuarios que está configurado en el archivo server.xml.
  • Si desea especificar un cookie LTPA específico, puede establecer un nombre de cookie LTPA personalizado y específico para cada servidor, estableciendo el siguiente atributo ssoCookieName. En este caso, el servidor solo genera y verifica el cookie LTPA específico que está configurado. Cualquier otro cookie LTPA generado por otros servidores no se utiliza.
    <webAppSecurity useOnlyCustomCookieName="true", ssoCookieName="myServerXCookieName" />  ------> Change the cookieName and make it unique across all the servers
  • Si el cookie LTPA generado desde un servidor JSR 375 o un servidor JASPIC se presenta a un servidor que no es JSR 375 ni JASPIC, se valida y utiliza para el inicio de sesión único sin preguntar al usuario.