![[z/OS]](../images/ngzos.gif)
Módulos de Mapeamento de LDAP de Um para UM para o System Authorization Facility (SAF) Customizados
Você pode customizar configurações de login do JAAS (Java™ Authentication and Authorization) gravando um módulo de mapeamento de login customizado.
O WebSphere Application Server for z/OS pode fornecer mapeamento a partir de uma solicitação de entrada do remote method invocation (RMI) com as credenciais LDAP para a Identidade do System Authorization Facility (SAF). O caso de uso é um WebSphere Application Server em qualquer plataforma configurada para o LDAP e envia uma solicitação RMI/IIOP para um segundo servidor configurado para um Registro do Usuário SAF. O WebSphere Application Server (liberação 5.1 ou posterior) envia uma solicitação de RMI usando o Token LTPA, representando a identidade LTPA para o WebSphere Application Server para z/OS que é configurado para o SAF. A figura a seguir ilustra este mapeamento.

A amostra a seguir é um módulo de login de entrada de RMI de JAAS que abre o token de LTPA, extrai o ID do usuário de LDAP, retira o cn=userID e configura WSCredential para o ID do usuário de SAF. O módulo de login de entrada de amostra que é configurado para o segundo servidor é suportado apenas no WebSphere Application Server para z/OS liberação 6.1 ou posterior.
A amostra na verdade é um módulo de login de hash no qual o com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID é definido como o z/OS SAF Identity desejado. O ltpaLoginModule verifica um com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID que foi transmitido e usa o ID especificado para criar o contexto de segurança apropriado para o usuário.
Esse módulo de amostra é configurado como um módulo de login RMI_INBOUND JAAS e deve ser especificado no topo da lista seguida pelo ltpaLoginModule.
Na amostra a seguir, uma identidade Java Platform, Enterprise Edition (Java EE) está disponível para controlar o mapeamento para uma identidade do SAF. O modelo usa o login de hash do valor Constants.WSCREDENTIAL no estado compartilhado.
// Este programa pode ser usado, executado, copiado, modificado e
// distribuído sem royalty para a finalidade de desenvolvimento,
// utilização, marketing ou distribuição.
//
//
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;
import com.ibm.wsspi.security.token.WSSecurityPropagationHelper;
import com.ibm.websphere.security.auth.callback.WSCredTokenCallbackImpl;
import com.ibm.websphere.security.auth.WSLoginFailedException;
/**
* **
* SampleLTPASAFMappingModule demonstra um módulo de login customizado
* que mapeia o Token LTPA existente do estado compartilhado para ID EXCLUSIVO para evitar o Login.
* O uso normal é um Token LTPA de entrada gerado de uma região diferente (talvez uma Região não zOS)
* para um Registro de Usuário SAF.
* O Token LTPA é usado como o token de identidade confiável.
*
* ***
*
* @author IBM Corporation
* @version 1.0
* @since 1.0
*/
public class SampleLTPASAFMappingModule implements LoginModule
{
/*
* **
* Constante que representa o nome desse módulo de mapeamento. Sempre que essa amostra
* de código for utilizada para criar uma classe com um nome diferente, esse valor deve ser alterado.
*
* Por padrão, esse valor é utilizado como parte da amostra do token de auditoria, e para
* propósitos de depuração.
* ***
*/
private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.security.SampleLTPASAFMappingModule";
private final static int MAXIMUM_NAME_LENGTH = 8;
/*
* **
* Especifica se a depuração é ativada para esse Módulo de Login. Isso depende do
* valor da opção "debug" transmitida pelo LoginContext.
* ***
*/
private boolean debugEnabled = false;
/*
* **
* Especifica a região de entrada que este Módulo de Login deve manipular. Se não houver nenhuma configuração
* de propriedade de entrada para onlyThisRealm, o Módulo de Login processará todo o pedido de entrada
* independentemente da região da identidade de entrada no token de LTPA.
* ***
*/
private String onlyThisRealm = null;
/*
* **
* Armazena o Assunto transmitido a partir do LoginContext.
* ***
*/
private Subject subject;
/*
* **
* Armazena o CallbackHandler transmitido pelo LoginContext.
* ***
*/
private CallbackHandler callbackHandler;
/*
* **
* Armazena o Mapa de estado compartilhado transmitido pelo LoginContext.
* ***
*/
private Map sharedState;
/*
* **
* Armazena o Mapa de opções transmitido pelo LoginContext.
* ***
*/
private Map options;
/*
* **
* Esse valor é utilizado para armazenar o sucesso ou falha do método login() para
* que commit() e abort() possam agir de modo diferente nos dois casos, se desejado.
* ***
*/
private boolean succeeded = false;
/**
* **construir um objeto do módulo de mapeamento não inicializado.***
*/
public SampleLTPASAFMappingModule()
{
}
/**
* **Inicializar este módulo de login.***
*
* **
* Ele é chamado pelo LoginContext depois que esse módulo de login é
* instanciado. As informações relevantes são transmitidas do LoginContext
* para esse módulo de login. Se o módulo de login não entender os dados
* armazenados no sharedState e parâmetros opcionais,
* eles podem ser ignorados.
* ***
*
* @param subject
* O assunto que esse LoginContext está autenticando
* @param callbackHandler
* Um CallbackHandler para se comunicar com o usuário final para reunir informações de login (por exemplo, nome de usuário e senha).
* @param sharedState
* O estado compartilhado com outros módulos de login configurados.
* @param options
* As opções especificadas nas configurações de login desse módulo de login específico.
*/
public void initialize(Subject newSubject, CallbackHandler newCallbackHandler, Map newSharedState, Map newOptions)
{
// obtenha primeiramente o valor para depuração, para que o rastreio possa ser utilizado
// nesse 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");
}
// obtenha o valor para a região antes de tudo para saber se este módulo de login deverá
// processar somente uma região de entrada em particular ou se deverá processar toda a região. Um valor nulo
// seleciona todas as regiões a serem processadas.
if (newOptions.containsKey("realm"))
{
onlyThisRealm = (String) newOptions.get("realm");
onlyThisRealm = onlyThisRealm.trim();
}
// nenhum desses objetos será utilizado por esse módulo de login, com exceção do sharedState,
// mas para manter consistência com a maioria dos módulos de login, manteremos uma referência a todos eles
this.subject = newSubject;
this.callbackHandler = newCallbackHandler;
this.sharedState = newSharedState;
this.options = newOptions;
if (debugEnabled)
{
debug(new Object[] { "initialize() exit", subject, callbackHandler, sharedState, options });
}
}
/**
*
* @retornar true se a autenticação for bem-sucedida ou false
* se esse Módulo de Login deve ser ignorado
* @exception LoginException
* se a autenticação falhar, o que é impossível para esse Módulo de Login
*/
public boolean login() throws LoginException
{
if (debugEnabled)
{
debug("login() entry");
}
// Manipular o retorno de chamada
javax.security.auth.callback.Callback callbacks[] =
new javax.security.auth.callback.Callback[1];
callbacks[0] = new com.ibm.websphere.security.auth.callback.WSCredTokenCallbackImpl("");
try
{
callbackHandler.handle(callbacks);
}
catch (Exception e)
{
if (debugEnabled)
{
debug(new Object[] { "Caught exception in callbackhandler: ", e });
}
return false;
}
byte[] credToken = ((WSCredTokenCallbackImpl) callbacks[0]).getCredToken();
String uid = null;
String realm = null;
String uniqueID = null;
//Esta rotina processará apenas um token de LTPA. Se não houver nenhum token de entrada,
//essa rotina retornará true e permitirá que outro LM manipule
//this request
if (credToken != null)
{
try
{
uniqueID = WSSecurityPropagationHelper.validateLTPAToken(credToken);
realm = WSSecurityPropagationHelper.getRealmFromUniqueID (uniqueID);
uid = createSAFIdenityName (uniqueID);
if (debugEnabled)
{
debug("using uniqueID: "+ uniqueID+ " inbound realm: "+ realm +
"uid: " + uid );
}
}
catch (Exception e)
{
if (debugEnabled)
{
debug(new Object[] { "Caught exception in callbackhandler: ", e });
}
return false;
}
}
else return true; // let the other LM handle this request.
//onlyThisRealm é uma configuração de propriedade de entrada para saber se desejamos este módulo de login
//para manipular somente 1 pedido de região inserido. Se onlyThisRealm for nulo,
//processaremos todos os pedidos e continuaremos. Se onlyThisRealm não
//corresponder ao pedido de entrada extraído de LTPA, deixaremos o outro
//LM manipular este pedido.
//
if ((onlyThisRealm != null) && (!realm.trim().equals(onlyThisRealm))) {
if (debugEnabled)
{
debug("inbound realm of "+realm+" does not match option realm of "+onlyThisRealm);
}
return true;
}
try {
// Recupera o InitialContext padrão para esse servidor.
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
// Recupera o objeto UserRegistry local.
com.ibm.websphere.security.UserRegistry reg =
(com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
// Recupera o uniqueID de registro com base no uid especificado
// no NameCallback.
String uniqueid = reg.getUniqueUserId(uid);
if (debugEnabled)
{
debug("uniqueid "+uniqueid);
}
// Recupera o nome de exibição do registro do usuário com base no uniqueID.
String securityName = reg.getUserSecurityName(uid);
if (debugEnabled)
{
debug("securityName "+securityName);
}
// Recupera os grupos associados a esse uniqueID.
java.util.List groupList = reg.getUniqueGroupIds(uid);
// Cria java.util.Hashtable com as informações reunidas
// do UserRegistry.
// Ao configurar esta tabela de hash, o módulo de login de LTPA configurará
// a Identidade de SAF corretamente.
java.util.Hashtable hashtable = new java.util.Hashtable();
hashtable.put(com.ibm.wsspi.security.token.AttributeNameConstants.
WSCREDENTIAL_UNIQUEID, uniqueid);
hashtable.put(com.ibm.wsspi.security.token.AttributeNameConstants.
WSCREDENTIAL_SECURITYNAME, securityName);
hashtable.put(com.ibm.wsspi.security.token.AttributeNameConstants.
WSCREDENTIAL_GROUPS, groupList);
// Inclui a tabela hash para o estado compartilhado do Subject.
sharedState.put(com.ibm.wsspi.security.token.AttributeNameConstants.
WSCREDENTIAL_PROPERTIES_KEY,hashtable);
}
catch (Exception e)
{
if (debugEnabled)
{
debug(new Object[] { "Caught exception in callbackhandler: ", e });
}
WSLoginFailedException e2 = new WSLoginFailedException("SampleLTPASAFMappingModule detected an error. "+e);
throw e2;
}
if (debugEnabled)
{
debug("login() exit");
}
return succeeded;
}
/**
* **Método para confirmar o resultado de autenticação.***
*
* **
* Esse Módulo de Login não precisa consolidar dados, portanto simplesmente retornaremos.
* ***
*
* @retornar true se o login original for bem-sucedido, ou false
* se o login original falhar
* @exception LoginException
* se a consolidação falhar, o que não pode ocorrer nesse Módulo de Login
*/
public boolean commit() throws LoginException
{
if (debugEnabled)
{
debug("commit() entry");
}
// o valor de retorno de commit() é igual ao sucesso do login original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("commit() exit");
}
return returnVal;
}
/**
* **Método para abortar o processo de autenticação (Fase 2).***
*
* **
* Independente de nosso login original ter tido sucesso ou falhado, esse método limpa
* nosso estado e retorna.
* ***
*
* @retornar true se o login original for bem-sucedido, ou false
* se o login original falhar
* @exception LoginException
* se a interrupção falhar, o que não pode ocorrer nesse Módulo de Login
*/
public boolean abort() throws LoginException
{
if (debugEnabled)
{
debug("abort() entry");
}
// o valor de retorno de abort() é igual ao sucesso do login original
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("abort() exit");
}
return returnVal;
}
/**
* **Método que faz logout de um Subject.***
*
* **
* Como nosso método commit não modificou o Subject, não há do que
* efetuar logout ou limpar e podemos apenas retornar true.
* ***
*
* @retornar true se o logout for bem-sucedido
* @exception LoginException
* se o logout falhar, o que não pode ocorrer no Módulo de Login
*/
public boolean logout() throws LoginException
{
if (debugEnabled)
{
debug("logout() entry");
}
// nossas variáveis locais foram limpas durante o commit, portanto não há necessidade de limpeza adicional
if (debugEnabled)
{
debug("logout() exit");
}
// como não há do que efetuar logout, sempre obtemos sucesso
return true;
}
/*
* **
* Limpa nossas variáveis locais; a única limpeza requerida para
* esse Módulo de Login é definir nossa variável de sucesso novamente para false.
* ***
*/
private void cleanup()
{
if (debugEnabled)
{
debug("cleanup() entry");
}
// não há o que limpar, portanto iremos apenas reconfigurar nossa variável de sucesso
succeeded = false;
if (debugEnabled)
{
debug("cleanup() exit");
}
}
/*
* **
* Método privado para imprimir informações de rastreio. A implementação utiliza System.out
* para imprimir informações de rastreio de saída padrão, mas um sistema de rastreio personalizado pode
* ser implementado aqui também.
* ***
*/
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);
}
}
}
/*
* **
* TODO
* Método particular para gerar a Identidade de SAF da identidade de LDAP. Talvez seja necessário
* modificar essa rotina por seus padrões de instalação para extrair a Identidade de SAF da
* Identidade de LDAP. Ou essa rotina pode ser escrita para mapear LDAP para a identidade de SAF.
* ***
*/
private String createSAFIdenityName(String name)
{
if (debugEnabled)
{
debug("createSAFIdenityName() entry");
}
if (debugEnabled)
{
debug("Using name='" + name + "' from principal");
}
name = name.toUpperCase();
int index = name.indexOf("/") + 1; // índice do primeiro caractere após o primeiro /
System.out.println(index);
System.out.println(name);
if (index >= name.length())
{
// esse bloco manipula o caso em que o primeiro / é o último caractere da Cadeia,
// isso realmente não deve ocorrer, mas se ocorrer podemos apenas o ignorar
name = name.substring(0, index - 1);
if (debugEnabled)
{
debug("Stripping trailing / from name");
}
}
else
{
// o índice é 0 (se não houver / no nome), ou é a posição
// após o primeiro / no nome
//
// de qualquer forma, iremos tirar a sub-cadeia desse ponto até o final da cadeia
name = name.substring(index);
}
System.out.println(name);
if (name.indexOf("CN=") >= 0)
name = name.substring((name.indexOf("CN=")+3),name.length());
if (name.indexOf(",") > 0)
name = name.substring(0,name.indexOf(","));
// reduzir o nome se seu comprimento exceder o máximo definido
if (name.length() > MAXIMUM_NAME_LENGTH)
{
name = name.substring(0, MAXIMUM_NAME_LENGTH);
if (debugEnabled)
{
debug("WSPrincipal name shortened to " + name);
}
}
if (debugEnabled)
{
debug("createSAFIdenityName() exit");
}
return name; //retorne um Nome de Identidade SAF
}
}
O módulo de mapeamento de amostra cria um mapeamento de um pedido de entrada de RMI com credenciais de LDAP para a identidade de SAF. Este módulo pode ser customizado para desempenhar o mapeamento.