![[z/OS]](../images/ngzos.gif)
Angepasste Module für die 1:1-Zuordnung von LDAP und SAF
Sie können JAAS-Anmeldekonfigurationen (Java™ Authentication and Authorization) anpassen, indem Sie ein angepasstes Anmeldezuordnungsmodul schreiben.
WebSphere Application Server for z/OS ist in der Lage, eine eingehende RMI-Anforderung (Remote Method Invocation) mit LDAP-Berechtigungsnachweisen einer SAF-Identität (System Authorization Facility) zuzuordnen. Der Anwendungsfall ist ein WebSphere Application Server auf einer beliebigen Plattform, in dem LDAP konfiguriert ist und der eine RMI/IIOP-Anforderung an einen zweiten Server sendet, in dem die Verwendung einer SAF-Benutzerregistry konfiguriert ist. WebSphere Application Server (ab Release 5.1) sendet eine RMI-Anforderung mit einem LTPA-Token, das die LDAP-Identität darstellt, an den WebSphere Application Server for z/OS, in dem SAF konfiguriert ist. Die folgende Abbildung veranschaulicht diese Zuordnung.

Das folgende Beispiel zeigt ein JAAS-RMI-Anmeldemodul für eingehende Anforderungen, das das LTPA-Token öffnet, die LDAP-Benutzer-ID extrahiert, "cn=userID" kürzt und WSCredential auf die SAF-Benutzer-ID setzt. Das für den zweiten Server konfigurierte Beispielanmeldemodul für eingehende Anforderungen wird nur in WebSphere Application Server for z/OS ab Release 6.1 unterstützt.
Das Beispiel ist eigentlich ein Hash-Anmeldmodul, in dem com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID auf die gewünschte z/OS-SAF-Identität gesetzt ist. Das ltpaLoginModule sucht nach einer com.ibm.wsspi.security.token.AttributeNameConstants.WSCREDENTIAL_UNIQUEID, die übergeben wurde, und verwendet die angegebene ID, um den entsprechenden Sicherheitskontext für den Benutzer zu erstellen.
Dieses Beispielmodul ist als JAAS-Anmeldemodul vom Typ "RMI_INBOUND" konfiguriert und muss ganz oben in der Liste, gefolgt von ltpaLoginModule, angegeben werden.
Im folgenden Beispiel ist eine Java EE-ID (Java Platform, Enterprise Edition) verfügbar, um die Zuordnung zu einer SAF-Identität zu steuern. In dem Beispiel wird die Hashanmeldung des Constants.WSCREDENTIAL-Wertes im gemeinsam genutzten Status ("shared") verwendet.
// Dieses Programm darf für Entwicklungs-, Nutzungs-, Marketing- und
// Vertriebszwecke gebührenfrei genutzt, ausgeführt, kopiert, geändert
// und vertrieben werden.
//
//
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 zeigt ein angepasstes Anmeldemodul, dass das vorhandene
* LTPA-Token aus dem gemeinsam genutzten Status einer eindeutigen ID zuordnet, um die
* Anmeldung zu vermeiden.
Gewöhnlich wird ein eingehendes LTPA-Token aus einem anderen
* Realm (unter Umständen kein z/OS-Realm) in einer SAF-Benutzerregistry generiert.
* Das LTPA-Token wird als anerkanntes Identitätstoken verwendet.
*
* ***
*
* @author IBM Corporation
* @version 1.0
* @since 1.0
*/
public class SampleLTPASAFMappingModule implements LoginModule
{
/*
* **
* Konstante, die den Namen dieses Zuordnungsmoduls repräsentiert. Wenn mit diesem
* Beispielcode eine Klasse anderen Namens erstellt wird, muss dieser Wert geändert werden.
*
* Dieser Wert wird standardmäßig als Teil des Beispielprüftoken und für das Debugging
* verwendet.
* ***
*/
private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.security.SampleLTPASAFMappingModule";
private final static int MAXIMUM_NAME_LENGTH = 8;
/*
* **
* Gibt an, ob für dieses Anmeldemodul das Debugging aktiviert ist. Dies
* hängt vom Wert Option "debug" ab, den der LoginContext übergeben hat.
* ***
*/
private boolean debugEnabled = false;
/*
* **
* Gibt den eingehenden Realm an, den dieses Anmeldmodul bearbeiten soll. Wenn keine
* Eigenschaftseinstellung für eingehende Anforderungen für onlyThisRealm vorhanden ist, verarbeitet dieses
* Anmeldmodul alle eingehenden Anforderungen, unabhängig vom eingehenden Identitätsrealm im LTPA-Token.
* ***
*/
private String onlyThisRealm = null;
/*
* **
* Speichert das vom LoginContext übergebene Subject.
* ***
*/
private Subject subject;
/*
* **
* Speichert den vom LoginContext übergebenen CallbackHandler.
* ***
*/
private CallbackHandler callbackHandler;
/*
* **
* Speichert die vom LoginContext übergebene Map für gemeinsamen Status.
* ***
*/
private Map sharedState;
/*
* **
* Speichert die vom LoginContext übergebene Optionszuordnung.
* ***
*/
private Map options;
/*
* **
* Mit diesem Wert wird der Erfolg oder das Scheitern der Methode login() gespeichert,
* damit commit() und abort() bei Bedarf entsprechend ausgeführt werden können.
* ***
*/
private boolean succeeded = false;
/**
* **Nicht initialisiertes Zuordnungsmodulobjekt erstellen.***
*/
public SampleLTPASAFMappingModule()
{
}
/**
* **Dieses Anmeldemodul initialisieren.***
*
* **
* Dieser Aufruf wird vom LoginContext nach der Instanzierung dieses
* Anmeldemoduls ausgeführt. Die relevanten Informationen werden vom
* LoginContext an dieses Anmeldemodul übergeben. Falls das Anmeldemodul
* die in den Parametern sharedState und options gespeicherten Daten
* nicht versteht, können diese ignoriert werden.
* ***
*
* @param subject
* das von diesem LoginContext authentifizierte Subject
* @param callbackHandler
* Ein CallbackHandler für die Kommunikation mit dem
* Endbenutzer zum Abrufen der Anmeldeinformationen
* (Benutzername und Kennwort)
* @param sharedState
* Der mit anderen konfigurierten Anmeldemodulen gemeinsam genutzte Status.
* @param options
* Die in der Anmeldekonfiguration für dieses spezielle Anmeldemodul angegebenen Optionen
*/
public void initialize(Subject newSubject, CallbackHandler newCallbackHandler, Map newSharedState, Map newOptions)
{
// Abrufen des Wertes für das Debug vor Ausführung aller weiteren Schritte, damit in dieser
// Methode der Trace verwendet werden kann
if (newOptions.containsKey("debug"))
{
String debugEnabledString = (String) newOptions.get("debug");
if (debugEnabledString != null && debugEnabledString.toLowerCase().equals("true"))
{
debugEnabled = true;
}
}
if (debugEnabled)
{
debug("initialize() entry");
}
// Wert für den Realm zuerst abrufen, um festzustellen, ob dieses Anmeldemodul nur einen
// bestimmten eingehenden Realm oder alle Realms verarbeiten soll. Ein Nullwert gibt an,
// dass alle Realms verarbeitet werden.
if (newOptions.containsKey("realm"))
{
onlyThisRealm = (String) newOptions.get("realm");
onlyThisRealm = onlyThisRealm.trim();
}
// Dieses Anmeldemodul verwendet keines dieser Objekte mit Ausnahme von sharedState. Aus Gründen
// der Konsistenz mit den meisten Anmeldemodulen wird eine Referenz auf alle Objekte gespeichert.
this.subject = newSubject;
this.callbackHandler = newCallbackHandler;
this.sharedState = newSharedState;
this.options = newOptions;
if (debugEnabled)
{
debug(new Object[] { "initialize() exit", subject, callbackHandler, sharedState, options });
}
}
/**
*
* @return true, wenn die Authentifizierung erfolgreich ist, oder false,
* wenn das Anmeldemodul ignoriert werden soll.
* @exception LoginException
* wenn die Authentifizierung scheitert, was bei diesem Anmeldemodul nicht möglich ist
*/
public boolean login() throws LoginException
{
if (debugEnabled)
{
debug("login() entry");
}
// Callback bearbeiten
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;
// Diese Routine verarbeitet nur ein LTPA-Token. Wenn kein eingehendes Token
// vorhanden ist, gibt diese Routine "true" zurück und lässt die Bearbeitung
// dieser Anforderung durch das andere Anmeldemodul zu.
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; // Die Anforderung vom anderen Anmeldemodul bearbeiten lassen.
// onlyThisRealm ist eine Eigenschaftseinstellung für eingehende Anforderungen, mit der festgestellt
// werden kann, ob dieses Anmeldemodul nur eine eingegebene Realmanforderung bearbeiten soll.
// Wenn onlyThisRealm null ist, werden alle Anforderungen verarbeitet. Wenn
// onlyThisRealm nicht mit der eingehenden Anforderung übereinstimmt, die aus LTPA extrahiert
// wird, wird die Verarbeitung dieser Anforderung durch das andere Anmeldemodul zugelassen.
//
if ((onlyThisRealm != null) && (!realm.trim().equals(onlyThisRealm))) {
if (debugEnabled)
{
debug("inbound realm of "+realm+" does not match option realm of "+onlyThisRealm);
}
return true;
}
try {
// Standard-InitialContext für den Server abrufen
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
// Lokales UserRegistry-Objekt abrufen
com.ibm.websphere.security.UserRegistry reg =
(com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
// Ruft die uniqueID der Registry ab, die auf dem im
// NameCallback angegebenen Benutzer-ID basiert.
String uniqueid = reg.getUniqueUserId(uid);
if (debugEnabled)
{
debug("uniqueid "+uniqueid);
}
// Ruft den auf der uniqueID basierenden Anzeigenamen von der Benutzerregistry ab.
String securityName = reg.getUserSecurityName(uid);
if (debugEnabled)
{
debug("securityName "+securityName);
}
// Gruppen für diese eindeutige ID abrufen
java.util.List groupList = reg.getUniqueGroupIds(uid);
// Erstellt die java.util.Hashtable mit den Informationen, die Sie
// in der UserRegistry-Implementierung erfasst haben.
// Durch die Definition dieser Hashtabelle kann das LTPA-Anmeldemodul die
// SAF-Identität ordnungsgemäß setzen.
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);
// Hashtabelle dem gemeinsam genutzten Status des Subject hinzufügen
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;
}
/**
* **Methode zum Festschreiben des Authentifizierungsergebnisses.***
*
* **
* Dieses Anmeldemodul muss keine Daten festschreiben, sodass es zurückkehren kann.
* ***
*
* @return true, wenn die ursprüngliche Anmeldung erfolgreich ist, oder false
* beim Scheitern der ursprünglichen Anmeldung
* @exception LoginException
* wenn das Festschreiben scheitert, was bei diesem Anmeldemodul nicht möglich ist
*/
public boolean commit() throws LoginException
{
if (debugEnabled)
{
debug("commit() entry");
}
// Der Rückgabewert von commit() entspricht einer erfolgreichen ursprünglichen Anmeldung.
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("commit() exit");
}
return returnVal;
}
/**
* **Methode zum Abbrechen des Authentifizierungsprozesses (Phase 2).***
*
* **
* Diese Methode bereinigt unabhängig vom Erfolg oder Scheitern der ursprünglichen
* Anmeldung den Status und kehrt zurück.
* ***
*
* @return true, wenn die ursprüngliche Anmeldung erfolgreich ist, oder false
* beim Scheitern der ursprünglichen Anmeldung
* @exception LoginException
* wenn der Abbruch scheitert, was bei diesem Anmeldemodul nicht möglich ist
*/
public boolean abort() throws LoginException
{
if (debugEnabled)
{
debug("abort() entry");
}
// Der Rückgabewert von abort() entspricht einer erfolgreichen ursprünglichen Anmeldung.
boolean returnVal = succeeded;
cleanup();
if (debugEnabled)
{
debug("abort() exit");
}
return returnVal;
}
/**
* **Methode zum Abmelden von einem Subject.***
*
* **
* Da das Subject von der Methode commit nicht geändert wurde, ist keine Abmeldung
* oder Bereinigung erforderlich. Das Modul kann zurückkehren.
* ***
*
* @return true bei erfolgreicher Abmeldung
* @exception LoginException
* wenn die Abmeldung scheitert, was bei diesem Anmeldemodul nicht möglich ist
*/
public boolean logout() throws LoginException
{
if (debugEnabled)
{
debug("logout() entry");
}
// Die lokalen Variablen wurden beim Festschreiben bereinigt. Es ist keine weitere Bereinigung erforderlich.
if (debugEnabled)
{
debug("logout() exit");
}
// Da keine Abmeldung erforderlich ist, ist nur ein Erfolg möglich.
return true;
}
/*
* **
* Bereinigung der lokalen Variablen. Für dieses Anmeldemodul muss nur
* die Variable für Erfolg zurück auf false gesetzt werden.
* ***
*/
private void cleanup()
{
if (debugEnabled)
{
debug("cleanup() entry");
}
// Es ist tatsächlich keine Bereinigung notwendig, daher wird einfach die Variable für Erfolg zurückgesetzt.
succeeded = false;
if (debugEnabled)
{
debug("cleanup() exit");
}
}
/*
* **
* Private Methode zum Ausgeben von Traceinformationen. Diese Implementierung verwendet
* System.out für die Ausgabe der Traceinformationen an die Standardausgabe. Sie können
* hier auch ein eigenes Tracesystem implementieren.
* ***
*/
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);
}
}
}
/*
* **
* Unerledigte Aufgabe
* Private Methode zum Generieren der SAF-Identität aus der LDAP-Identität. Diese Routine muss
* möglicherweise von den Installationsstandards geändert werden, um die SAF-Idenität aus
* der LDAP-Identität zu extrahieren. Die Routine kann auch so geschrieben werden, dass die
* LDAP-Identität der SAF-Identität zugeordnet wird.
* ***
*/
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; // Index des ersten Zeichens nach dem ersten /
System.out.println(index);
System.out.println(name);
if (index >= name.length())
{
// Diesen Block gibt es für den Fall, dass der erste / das letzte Zeichen der String ist.
// In diesem höchst unwahrscheinlichen Fall wird das Zeichen einfach gelöscht.
name = name.substring(0, index - 1);
if (debugEnabled)
{
debug("Stripping trailing / from name");
}
}
else
{
// Der Index ist 0 (wenn der Name keinen / enthält) oder gibt die
// Position des ersten / im Namen an.
//
// Hier wird in jedem Fall die Unterzeichenfolge von diesem Punkt bis zum Ende der Zeichenfolge verwendet.
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(","));
// Kürzen des Namens, falls er die definierte Maximallänge überschreitet
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; //Namen der SAF-Identität zurückgeben
}
}
Das Beispielzuordnungsmodul erstellt eine Zuordnung aus einer eingehenden RMI-Anforderung mit LDAP-Berechtigungsnachweisen zu einer SAF-Identität. Dieses Modul kann so angepasst werden, dass es die Zuordnung durchführt.