![[z/OS]](../images/ngzos.gif)
Módulos de correlación de SAF (System Authorization Facility) personalizados
Puede personalizar las configuraciones de inicio de sesión JAAS (Java™ Authentication and Authorization) escribiendo un módulo de correlación de inicio de sesión personalizado.
El módulo AuthenLoginModule y el módulo ltpaLoginModule de WebSphere Application Server utilizan el estado compartido para guardar información de estado, y permiten que los LoginModules modifiquen la información de estado. El módulo ltpaLoginModule inicializa la matriz de llamadas de retorno en el método login() mediante el código siguiente. ltpaLoginModule crea la matriz de retorno de llamada sólo si no hay una matriz definida en el área de estado compartido.
En el siguiente ejemplo de código, se confía en la disponibilidad de la identidad de Java Platform, Enterprise Edition (Java EE) para que controle la correlación con la identidad de SAF (System Authorization Facility). Este código utiliza el valor de Constants.WSPRINCIPAL_KEY en el estado compartido. El módulo de inicio de sesión de WebSphere Application Server 1 coloca el valor en el código. Puede insertar un LoginModule personalizado después del módulo ltpaLoginModule y justo antes del módulo MapPlatformSubject. Si lo hace, utilice una matriz de llamadas de retorno u otros valores de estado compartido para obtener un valor utilizado para controlar la correlación con el ID de usuario de z/OS.
//
// Este programa se puede utilizar, ejecutar, copiar, modificar y
// distribuir sin derechos de copia para fines de desarrollo,
// uso, comercialización o distribución.
//
//
package com.ibm.websphere.security;
import com.ibm.websphere.security.auth.CredentialDestroyedException;
import com.ibm.websphere.security.auth.WSPrincipal;
import com.ibm.websphere.security.cred.WSCredential;
import com.ibm.wsspi.security.auth.callback.Constants;
import com.ibm.wsspi.security.token.AttributeNameConstants;
import java.lang.reflect.Array;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.CredentialExpiredException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
*
* SampleSAFMappingModule demuestra un módulo de inicio de sesión personalizado
* que correlaciona el principal de WS existente del estado compartido con un ID
* de usuario de z/OS.
*
*
*
* Los valores siguientes se establecerán en el estado compartido si la autenticación
* es satisfactoria. Si la autenticación falla, este módulo de inicio de sesión aún indicará
* una ejecución satisfactoria, pero no se establecerán valores en el estado compartido.
*
* AttributeNameConstants.ZOS_USERID
* AttributeNameConstants.ZOS_AUDIT_STRING
* AttributeNameConstants.CALLER_PRINCIPAL_CLASS
*
* Este módulo de inicio de sesión no utiliza llamadas de retorno ni modifica el sujeto
* de ninguna forma.
*
* @author IBM Corporation
* @version 1.0
* @since 1.0
*/
public class SampleSAFMappingModule implements LoginModule
{
/*
* Constante que representa el nombre de este módulo de correlación. Si se utiliza
* este código de ejemplo para crear una clase con otro nombre, este valor debe cambiarse.
*
* De forma predeterminada, este valor se utiliza como parte del símbolo de auditoría de ejemplo y para
* depuraciones.
*/
private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.security.SampleSAFMappingModule";
/*
* Constante que representa la longitud máxima de ZOS_USERID. Las restricciones de denominación de MVS
* la limitan a ocho caracteres.
*
* Cuando se elige la opción useWSPrincipalName, el nombre del principal de WS se
* abrevia a estos caracteres.
*/
private final static int MAXIMUM_NAME_LENGTH = 8;
/*
* Especifica si debe utilizarse o no el funcionamiento de correlación predeterminada de este módulo, que
* es utilizar el nombre del principal de WS para generar el ZOS_USERID. Esto depende del
* valor de la opción "useWSPrincipalName" pasada desde LoginContext.
*/
private boolean useWSPrincipalName = true;
/*
* Especifica si la depuración está habilitada para este módulo de inicio de sesión. Esto depende del
* valor de la opción "debug" pasada desde LoginContext.
*/
private boolean debugEnabled = false;
/*
* Almacena el sujeto pasado desde LoginContext.
*/
private Subject subject;
/*
* Almacena CallbackHandler pasado desde LoginContext.
*/
private CallbackHandler callbackHandler;
/*
* Almacena la correlación de estado compartido pasada desde LoginContext.
*/
private Map sharedState;
/*
* Almacena la correlación de opciones pasada desde LoginContext.
*/
private Map options;
/*
* Este valor se utiliza para almacenar la ejecución satisfactoria o la anomalía del método login() para que
* los métodos commit() y abort() puedan actuar de forma distinta en los dos casos, si se desea.
*/
private boolean succeeded = false;
/**
* Construir un objeto de módulo de correlación no inicializado.
*/
public SampleSAFMappingModule()
{
}
/**
* Inicializar este módulo de inicio de sesión.
*
* LoginContext realiza una llamada a este método después de que se instancie
* este módulo de inicio de sesión. La información relevante se pasa de LoginContext
* a este módulo de inicio de sesión. Si el módulo de inicio de sesión no comprende los datos
* almacenados en los parámetros sharedState y options,
* se pueden ignorar.
*
* @param subject
* Sujeto en el que se autentica este LoginContext
* @param callbackHandler
* CallbackHandler para comunicarse con el usuario final
* para recopilar información de inicio de sesión (por ejemplo, nombre de usuario y contraseña).
* @param sharedState
* Estado compartido con otros módulos de inicio de sesión configurados.
* @param options
* Opciones especificadas en la configuración de inicio de sesión de este módulo de inicio de sesión concreto.
*/
public void initialize(Subject newSubject, CallbackHandler newCallbackHandler,
Map newSharedState, Map newOptions)
{
// obtenga el valor de la depuración antes que nada para que se pueda utilizar el rastreo dentro
// de este método
if (newOptions.containsKey("debug"))
{
String debugEnabledString = (String) newOptions.get("debug");
if (debugEnabledString != null && debugEnabledString.toLowerCase().equals("true"))
{
debugEnabled = true;
}
}
if (debugEnabled)
{
debug("initialize() entry");
}
// este módulo de inicio de sesión no va a utilizar ninguno de estos objetos excepto para el estado compartido,
// pero para obtener coherencia con la mayor parte de módulos de inicio de sesión, guardaremos una referencia a todos ellos
this.subject = newSubject;
this.callbackHandler = newCallbackHandler;
this.sharedState = newSharedState;
this.options = newOptions;
if (options.containsKey("useWSPrincipalName"))
{
String useWSPrincipalNameString = (String) options.get("useWSPrincipalName");
if (useWSPrincipalNameString != null && useWSPrincipalNameString.toLowerCase().equals("false"))
{
useWSPrincipalName = false;
}
}
if (debugEnabled)
{
debug(new Object[] { "initialize() exit", subject, callbackHandler, sharedState, options });
}
}
/**
* Método para correlación el principal de WS con un ID_USUARIO_ZOS
*
* Este método deriva un ZOS_USERID y lo almacena en el estado compartido para que lo utilice un
* módulo de inicio de sesión posterior.
*
* @return true si la autenticación ha sido satisfactoria o false
* si este módulo de inicio de sesión debe ignorarse
* @exception LoginException
* si la autenticación falla, lo cual es imposible para este módulo de inicio de sesión
*/
public boolean login() throws LoginException
{
if (debugEnabled)
{
debug("login() entry");
}
if (sharedState.containsKey(AttributeNameConstants.ID_USUARIO_ZOS))
{
// no deseamos alterar temporalmente este valor si, por algún motivo, otro módulo de inicio de sesión
// ya lo ha colocado en el estado compartido, pero aún lo considera como una ejecución satisfactoria
// porque los criterios de salida de este módulo se han cumplido
if (debugEnabled)
{
debug("ID_USUARIO_ZOS ya existe: no se necesita trabajo adicional");
}
succeeded = true;
}
else if (!sharedState.containsKey(Constants.CLAVE_PRINCIPAL_WS) ||
!sharedState.containsKey(Constants.CLAVE_CREDENCIAL_WS))
{
// si no hay ningún principal ni credencial en el estado compartido, no podemos
// hacer nada, por lo que devolveremos el valor false para informar a LoginContext de que ignore este módulo
if (debugEnabled)
{
debug("El principal o la credencial no está disponible: ignorando este módulo de inicio de sesión");
}
succeeded = false;
}
else
{
if (debugEnabled)
{
debug("El principal y la credencial están disponibles: continúa el inicio de sesión");
}
String name = null;
String audit = null;
String principalClass = null;
// extrae WSPrincipal y WSCredential del estado compartido
WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.CLAVE_PRINCIPAL_WS);
WSCredential credential = (WSCredential) sharedState.get(Constants.CLAVE_CREDENCIAL_WS);
if (useWSPrincipalName)
{
// este módulo de correlación de ejemplo proporciona un método para obtener ZOS_USERID directamente
// del nombre de principal de WS si la propiedad "useWSPrincipalName" se establece en true
if (debugEnabled)
{
debug("Utilizando el nombre de principal de WS para obtener ZOS_USERID");
}
name = createName(principal);
String realm = getRealm(credential);
// para este ejemplo, se creará un símbolo de auditoría de ejemplo que combine el ZOS_USERID,
// el reino y el nombre de este módulo de correlación
//
// se puede crear un símbolo de auditoría personalizado con los valores disponibles en lugar
de utilizar
// este símbolo de ejemplo
audit = realm + "/" + name + " MappingModule:" + MAPPING_MODULE_NAME;
// Un sujeto puede contener más de un principal. Este valor especifica la
// clase del principal que debe devolverse cuando se solicita al sujeto
// el principal llamante. Si una clase de principal personalizado se ha colocado
// en el sujeto, ese nombre de clase debe especificarse aquí.
//
// Se proporcionan dos valores predefinidos para la clase de principal llamante:
//
// - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
// clase de principal de z/OS
//
// - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
// clase de principal predeterminada
principalClass = AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS;
succeeded = true;
}
else
{
if (debugEnabled)
{
debug("Utilizando el nombre de lógica personalizada para obtener ZOS_USERID");
}
// Si el funcionamiento proporcionado por este módulo de correlación para obtener el ZOS_USERID del
// nombre de principal de WS no es suficiente, la lógica de correlación personalizada se puede proporcionar aquí.
//
// Para utilizar esta lógica de correlación personalizada, la propiedad "useWSPrincipalName" debe establecerse
// en false.
// name = ...lógica personalizada
// audit = ...lógica personalizada
// principalClass = ...lógica personalizada
// De forma predeterminada, no se proporciona ninguna correlación personalizada, por lo que la ejecución satisfactoria
// de esta vía de acceso tiene el valor false; si se proporciona la correlación personalizada, la siguiente variable
// debe modificarse para representar la ejecución satisfactoria o no de la correlación personalizada
succeeded = false;
}
if (succeeded)
{
// Ahora que tenemos valores para el nombre, la auditoría y la clase de principal, sólo tenemos
// que establecerlos en el estado compartido.
sharedState.put(AttributeNameConstants.ZOS_USERID, name);
sharedState.put(AttributeNameConstants.ZOS_AUDIT_STRING, audit);
sharedState.put(AttributeNameConstants.CALLER_PRINCIPAL_CLASS, principalClass);
if (debugEnabled)
{
debug(new Object[] { "Los valores se han almacenado en estado compartido", name,
audit, principalClass });
}
}
}
if (debugEnabled)
{
debug("login() exit");
}
return succeeded;
}
/**
* Método para comprometer el resultado de la autenticación.
*
* Este módulo de inicio de sesión no tiene que comprometer ningún dato, por lo que simplemente se devolverá.
*
* @return true si el inicio de sesión original ha sido satisfactorio o false
* si el inicio de sesión original ha fallado
* @exception LoginException
* si la acción de comprometer falla, lo que no puede suceder en este módulo de inicio de sesión
*/
public boolean commit() throws LoginException
{
if (debugEnabled)
{
debug("commit() entry");
}
// el valor de retorno de commit() es el mismo que para la ejecución satisfactoria del inicio de sesión original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("commit() exit");
}
return returnVal;
}
/**
* Método para terminar anormalmente el proceso de autenticación (fase 2).
*
* Independientemente de si el nuevo inicio de sesión original ha sido satisfactorio o no, este método borra
* nuestro estado y vuelve.
*
* @return true si el inicio de sesión original ha sido satisfactorio o false
* si el inicio de sesión original ha fallado
* @exception LoginException
* si la acción de finalización anormal falla, lo que no puede suceder en este módulo de inicio de sesión
*/
public boolean abort() throws LoginException
{
if (debugEnabled)
{
debug("abort() entry");
}
// el valor de retorno de abort() es el mismo que para la ejecución satisfactoria del inicio de sesión original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("abort() exit");
}
return returnVal;
}
/**
* Método que finaliza la sesión de un sujeto.
*
* Puesto que nuestro método de compromiso no ha modificado el sujeto, no tenemos que
* finalizar sesión ni borrar los datos y sólo se devolverá true.
*
* @return true si la finalización de sesión ha sido satisfactoria
* @exception LoginException
* si la acción de finalización de sesión falla, lo que no puede suceder en el módulo de inicio de sesión
*/
public boolean logout() throws LoginException
{
if (debugEnabled)
{
debug("logout() entry");
}
// nuestras variables locales se borran durante la acción de compromiso, por lo que no se necesita ningún borrado más
if (debugEnabled)
{
debug("logout() exit");
}
// puesto que no hay ningún elemento cuya sesión deba finalizar, la ejecución siempre es satisfactoria
return true;
}
/*
* Borra nuestras variables locales; el único borrado obligatorio para
* este módulo de inicio de sesión es establecer la variable success de nuevo en false.
*/
private void cleanup()
{
if (debugEnabled)
{
debug("cleanup() entry");
}
// no hay ningún método que borrar realmente, por lo que debe restablecer simplemente la variable success
succeeded = false;
if (debugEnabled)
{
debug("cleanup() exit");
}
}
/*
* Método privado para imprimir la información de rastreo. Esta implementación utiliza System.out
* para imprimir la información de rastreo en la salida estándar, pero un sistema de rastreo personalizado puede
* implementarse aquí también.
*/
private void debug(Object o)
{
System.out.println("Debug: " + MAPPING_MODULE_NAME);
if (o != null)
{
if (o.getClass().isArray())
{
int length = Array.getLength(o);
for (int i = 0; i < length; i++)
{
System.out.println("\t" + Array.get(o, i));
}
}
else
{
System.out.println("\t" + o);
}
}
}
/*
* Método privado para obtener el nombre de reino de la credencial y devolverlo. Este
* conserva el manejo de excepciones que implica la obtención del nombre de reino de la
* lógica principal de login().
*/
private String getRealm(WSCredential credential)
{
if (debugEnabled)
{
debug("getRealm() entry");
}
String realm = null;
try
{
realm = credential.getRealmName();
if (debugEnabled)
{
debug("Se ha obtenido el reino='" + realm + "' de la credencial");
}
}
catch (Exception e)
{
// getRealmName genera CredentialExpiredException y CredentialDestroyedException
if (debugEnabled)
{
debug(new Object[] { "Se ha detectado una excepción en getRealm: ", e });
}
realm = "UNKNOWN_REALM";
}
if (debugEnabled)
{
debug("getRealm() exit");
}
return realm;
}
/*
* Método privado para generar el ZOS_USERID del nombre de principal de WS.
*/
private String createName(WSPrincipal principal)
{
if (debugEnabled)
{
debug("createName() entry");
}
String name = principal.getName();
if (debugEnabled)
{
debug("Utilizando el nombre='" + name + "' del principal");
}
// WSPrincipal.getName() puede devolver REALM/NAME, por lo que debe analizar la serie para obtener sólo el nombre
int index = name.indexOf("/") + 1; // índice del primer carácter después de la primera /
if (index >= name.length())
{
// este bloque maneja el caso en el que la primera / es el último carácter de la serie,
// no debería suceder, pero si sucede, simplemente se puede quitar
name = name.substring(0, index - 1);
if (debugEnabled)
{
debug("Quitando la / final del nombre de principal de WS");
}
}
else
{
// el índice es 0 (si no existe ninguna / en el nombre) o es la posición
// después de la primera / del nombre
//
// en ambos casos, adoptaremos la subserie a partir de ese punto hasta el
final
name = name.substring(index);
}
// abrevie el nombre si su longitud supera el máximo definido
if (name.length() > MAXIMUM_NAME_LENGTH)
{
name = name.substring(0, MAXIMUM_NAME_LENGTH);
if (debugEnabled)
{
debug("Nombre de principal de WS abreviado a " + name);
}
}
// Los ID de MVS son en mayúsculas
name = name.toUpperCase();
if (debugEnabled)
{
debug("createName() exit");
}
return name;
}
}
- Compile el código Java. Asegúrese de que el código sea fiable y de que se trate con el mismo cuidado que un módulo autorizado por APF. A la configuración de inicio de sesión del sistema JAAS (Java Authorization and Authentication Service) por omisión se accede desde el controlador de z/OS.
- Si especifica una clase de correlación distinta de la que proporciona por omisión IBM®, debe instalar la clase en el directorio de clases del servidor de aplicaciones y los gestores de despliegue. Coloque el archivo JAR (Java Archive) en el directorio WAS_HOME/classes para cada nodo de la célula, incluido el nodo del gestor de despliegue en una célula de WebSphere Application Server, Network Deployment.