![[z/OS]](../images/ngzos.gif)
Módulos de Mapeamento Customizado do System Authorization Facility
Você pode customizar configurações de login JAAS (Java™ Authentication and Authorization) gravando um módulo de mapeamento de login customizado.
O módulo WebSphere Application Server ltpaLoginModule e o módulo AuthenLoginModule usam o estado compartilhado para salvar informações de estado com a capacidade de permitir que LoginModules possa modificar informações de estado. O ltpaLoginModule inicializa a matriz de retorno de chamada no método login() utilizando o seguinte código. A matriz de retorno de chamada é criada pelo ltpaLoginModule apenas se uma matriz não estiver definida na área de estado compartilhado.
No exemplo de código a seguir, a confiança é estabelecida sobre a disponibilidade de uma identidade Java Platform, Enterprise Edition (Java EE) para controlar o mapeamento para uma identidade SAF (System Authorization Facility). Esse código utiliza o valor Constants.WSPRINCIPAL_KEY no estado compartilhado. O valor é colocado no código por um módulo de login do WebSphere Application Server 1. É possível inserir um LoginModule personalizado após o módulo ltpaLoginModule e antes do módulo MapPlatformSubject. Se você fizer isso, utilize uma matriz de retorno de chamada ou outros valores de estado compartilhado para obter um valor utilizado para controlar o mapeamento para o ID do usuário do z/OS.
//
// Este programa pode ser utilizado, 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;
/**
*
* SampleSAFMappingModule demonstra um módulo de login personalizado
* que mapeia o WSPrincipal existente do estado compartilhado para um
* id de usuário do z/OS.
*
*
*
* Os valores a seguir serão definidos para o estado compartilhado se a autenticação
* for bem-sucedida. Se a autenticação falhar, esse módulo de login ainda indicará
* sucesso, mas nenhum valor será definido para o estado compartilhado.
*
* AttributeNameConstants.ZOS_USERID
* AttributeNameConstants.ZOS_AUDIT_STRING
* AttributeNameConstants.CALLER_PRINCIPAL_CLASS
*
* Esse módulo de login não utiliza retornos de chamada ou modifica o Subject
* de forma alguma.
*
* @author IBM Corporation
* @version 1.0
* @since 1.0
*/
public class SampleSAFMappingModule 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.SampleSAFMappingModule";
/*
* Constante que representa o comprimento máximo de ZOS_USERID. Restrições atuais de
* nomenclatura do MVS limitam esse valor para oito caracteres.
*
* Quando a opção para useWSPrincipalName é escolhida, o nome do WSPrincipal é
* reduzido para essa quantidade de caracteres.
*/
private final static int MAXIMUM_NAME_LENGTH = 8;
/*
* Especifica se o comportamento de mapeamento padrão do módulo deve ou não ser utilizado, que é
* utilizar o nome WSPrincipal para gerar o ZOS_USERID. Isso depende do
* valor da opção "useWSPrincipalName" transmitida pelo LoginContext.
*/
private boolean useWSPrincipalName = true;
/*
* 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;
/*
* 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;
/**
* Construa um objeto de módulo de mapeamento não-inicializado.
*/
public SampleSAFMappingModule()
{
}
/**
* Inicialize esse 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 comunicação com o usuário final
* para coletar informações de login (ex:, nome do 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");
}
// 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 (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 mapear WSPrincipal para um ZOS_USERID
*
* Esse método deriva um ZOS_USERID e o armazena no Estado Compartilhado para ser utilizado por um
* Módulo de Login posterior.
*
* @retornar verdadeiro se a autenticação for bem-sucedida, ou falso
* 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");
}
if (sharedState.containsKey(AttributeNameConstants.ZOS_USERID))
{
// não queremos substituir esse valor se, por qualquer motivo, outro Módulo de Login
// já tiver o colocado no estado compartilhado, mas ainda o consideramos um sucesso
// porque os critérios de saída desse módulo foram atendidos
if (debugEnabled)
{
debug("ZOS_USERID already exists: so no additional work is needed");
}
succeeded = true;
}
else if (!sharedState.containsKey(Constants.WSPRINCIPAL_KEY) ||
!sharedState.containsKey(Constants.WSCREDENTIAL_KEY))
{
// se não houver um Proprietário ou uma Credencial no estado compartilhado, nada pode ser
// feito, portanto retornaremos false para informar ao LoginContext para ignorar esse módulo
if (debugEnabled)
{
debug("Principal or Credential is unavailable: skipping this Login Module");
}
succeeded = false;
}
else
{
if (debugEnabled)
{
debug("Principal and Credential are available: continue with login");
}
String name = null;
String audit = null;
String principalClass = null;
// extrair o WSPrincipal e o WSCredential do estado compartilhado
WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.WSPRINCIPAL_KEY);
WSCredential credential = (WSCredential) sharedState.get(Constants.WSCREDENTIAL_KEY);
if (useWSPrincipalName)
{
// esse módulo de amostra de mapeamento oferece um método para obter o ZOS_USERID diretamente
// do nome WSPrincipal se a propriedade "useWSPrincipalName" for definida para true
if (debugEnabled)
{
debug("Using name from WSPrincipal to obtain ZOS_USERID");
}
name = createName(principal);
String realm = getRealm(credential);
// nesse exemplo, uma amostra de token de auditoria será criada, combinando o ZOS_USERID,
// a região e o nome desse módulo de mapeamento
//
// um token de auditoria personalizado pode ser criado utilizando quaisquer dados disponíveis ao invés
// dessa amostra de token
audit = realm + "/" + name + " MappingModule:" + MAPPING_MODULE_NAME;
// Um Subject pode conter mais de um Proprietário. Esse valor especifica a
// classe do Proprietário a ser retornada quando o Subject é solicitado pelo
// Proprietário Responsável pela Chamada. Se uma classe de Proprietário personalizada tiver sido colocada no
// Subject, esse nome de classe pode ser especificado aqui.
//
// Dois valores pré-definidos para a Classe de Proprietário do Responsável pela Chamada são fornecidos:
//
// - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
// a classe do Proprietário do z/OS
//
// - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
// a classe de Proprietário padrão
principalClass = AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS;
succeeded = true;
}
else
{
if (debugEnabled)
{
debug("Using Custom logic to obtain ZOS_USERID");
}
// se o comportamento fornecido por esse módulo de mapeamento para obter ZOS_USERID do
// nome do WSPrincipal não for suficiente, lógica de mapeamento personalizada pode ser fornecida aqui
//
// para utilizar essa lógica de mapeamento personalizada, a propriedade "useWSPrincipalName" deve ser definida
// para false
// name = ...lógica personalizada
// audit = ...lógica personalizada
// principalClass = ...lógica personalizada
// por padrão, nenhum mapeamento personalizado é fornecido, portanto o sucesso desse caminho de código
// é falso; se mapeamento personalizado for fornecido, a variável a seguir deve ser
// modificada para representar o sucesso ou falha do mapeamento personalizado
succeeded = false;
}
if (succeeded)
{
// agora que temos valores para name, audit, e principalClass, precisamos apenas defini-los
// para o estado compartilhado
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[] { "Os valores foram armazenados no estado compartilhado", name,
audit, principalClass });
}
}
}
if (debugEnabled)
{
debug("login() exit");
}
return succeeded;
}
/**
* Método para consolidar o resultado da autenticação.
*
* Esse Módulo de Login não precisa consolidar dados, portanto simplesmente retornaremos.
*
* @retornar verdadeiro se o login original foi bem-sucedido, ou falso
* 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 interromper 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 verdadeiro se o login original foi bem-sucedido, ou falso
* 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 efetua 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 verdadeiro 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);
}
}
}
/*
* Método privado para obter o nome da região da Credencial e retorná-lo. Este
* mantém a manipulação da exceção envolvida com a obtenção do nome da região da
* lógica login() principal.
*/
private String getRealm(WSCredential credential)
{
if (debugEnabled)
{
debug("getRealm() entry");
}
String realm = null;
try
{
realm = credential.getRealmName();
if (debugEnabled)
{
debug("Got realm='" + realm + "' from credential");
}
}
catch (Exception e)
{
// getRealmName emite CredentialExpiredException e CredentialDestroyedException
if (debugEnabled)
{
debug(new Object[] { "Exceção obtida em getRealm: ", e });
}
realm = "UNKNOWN_REALM";
}
if (debugEnabled)
{
debug("getRealm() exit");
}
return realm;
}
/*
* Método particular para gerar o ZOS_USERID do nome WSPrincipal.
*/
private String createName(WSPrincipal principal)
{
if (debugEnabled)
{
debug("createName() entry");
}
String name = principal.getName();
if (debugEnabled)
{
debug("Using name='" + name + "' from principal");
}
// WSPrincipal.getName() pode retornar REALM/NAME, portanto analise a Cadeia para obter apenas o nome
int index = name.indexOf("/") + 1; // índice do primeiro caractere após o primeiro /
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 WSPrincipal 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);
}
// 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);
}
}
// ids MVS são todos em letras maiúsculas
name = name.toUpperCase();
if (debugEnabled)
{
debug("createName() exit");
}
return name;
}
}
- Compile o código Java. Certifique-se de que o código seja confiável e tratado com a mesma atenção que um módulo autorizado por APF. A configuração de login do sistema JAAS (Java Authorization and Authentication Service) padrão é acessada no controlador do z/OS.
- Se você especificar uma classe de mapeamento diferente do padrão fornecido pela IBM®, será necessário instalar a classe no diretório de classes do Servidor de Aplicativos e dos Gerenciadores de Implementação. Coloque o arquivo Java archive (JAR) no diretório WAS_HOME/classes para cada nó na célula, incluindo o nó do gerenciador de implementação em uma célula do WebSphere Application Server, Network Deployment.