![[z/OS]](../images/ngzos.gif)
Angepasste SAF-Zuordnungsmodule
Sie können JAAS-Anmeldekonfigurationen (Java™ Authentication and Authorization) anpassen, indem Sie ein angepasstes Anmeldezuordnungsmodul schreiben.
Die WebSphere Application Server-Module ltpaLoginModule und AuthenLoginModule verwenden den gemeinsamen Status, um Statusinformationen so zu speichern, dass sie von LoginModules geändert werden können. Das ltpaLoginModule initialisiert das Callback-Array in der Methode login() mit dem folgenden Code. Das Callback-Array wird vom ltpaLoginModule nur erstellt, wenn im Bereich für gemeinsamen Status kein Array definiert ist.
Im folgenden Codebeispiel wird die Zuordnung zu einer SAF-Identität mit einer Java EE-Identität (Java Platform, Enterprise Edition) gesteuert. Dieser Code verwendet den Wert Constants.WSPRINCIPAL_KEY im gemeinsamen Status. Der Wert wird von einem Anmeldemodul von WebSphere Application Server in den Code geschrieben. Sie können nach dem Modul ltpaLoginModule und unmittelbar vor dem Modul MapPlatformSubject ein eigenes LoginModule einfügen. Wenn Sie dies tun, verwenden Sie ein Callback-Array oder andere Werte aus dem gemeinsamen Status, um für die Steuerung der Zuordnung zur z/OS-Benutzer-ID einen Wert abzurufen.
//
// 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;
/**
*
* SampleSAFMappingModule demonstriert ein angepasstes Anmeldemodul,
* das den vorhandenen WSPrincipal aus dem gemeinsamen Status einer
* z/OS-Benutzer-ID zuordnet.
*
*
*
* Die folgenden Werte werden bei erfolgreicher Authentifizierung im gemeinsamen
* Status definiert. Scheitert die Anmeldung, meldet dieses Anmeldemodul trotzdem einen
* Erfolg. Es werden jedoch keine Werte im gemeinsamen Status definiert.
*
* AttributeNameConstants.ZOS_USERID
* AttributeNameConstants.ZOS_AUDIT_STRING
* AttributeNameConstants.CALLER_PRINCIPAL_CLASS
*
* Dieses Anmeldemodul verwendet keine Callbacks und modifiziert nicht das
* Subject.
*
* @author IBM Corporation
* @version 1.0
* @since 1.0
*/
public class SampleSAFMappingModule 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.SampleSAFMappingModule";
/*
* Konstante, die die maximale Länge der ZOS_USERID repräsentiert. Nach aktuellen
* MVS-Bedingungen für Namen ist die ID auf acht Zeichen begrenzt.
*
* Bei Auswahl von useWSPrincipalName wird der Name vom WSPrincipal auf
* diese Zeichenanzahl gekürzt.
*/
private final static int MAXIMUM_NAME_LENGTH = 8;
/*
* Gibt an, ob das Standardverhalten dieses Moduls angewendet werden soll, bei dem
* die ZOS_USERID aus dem WSPrincipal-Namen generiert wird. Dies hängt vom Wert der
* Option "useWSPrincipalName" ab, den der LoginContext übergeben hat.
*/
private boolean useWSPrincipalName = true;
/*
* 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;
/*
* 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;
/**
* Ein nicht initialisiertes Objekt für das Zuordnungsmodul erstellen
*/
public SampleSAFMappingModule()
{
}
/**
* Das 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");
}
// 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 (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 });
}
}
/**
* Methode für die Zuordnung des WSPrincipal zu einer ZOS_USERID
*
* Diese Methode leitet eine ZOS_USERID ab und speichert sie im gemeinsamen Status
* für eines der folgenden Anmeldemodule.
*
* @return true bei erfolgreicher Authentifizierung oder false
* falls dieses 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");
}
if (sharedState.containsKey(AttributeNameConstants.ZOS_USERID))
{
// Dieser Wert wird nicht überschrieben, weil er von einem anderen Anmeldemodul bereits
// in den gemeinsamen Status gestellt worden sein könnte. Dies wird dennoch als Erfolg
// gewertet, weil die Exit-Bedingungen für dieses Modul erfüllt wurden.
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))
{
// Falls es keinen Principal oder Berechtigungsnachweis im gemeinsamen Status gibt, wird
// false zurückgegeben, um dem LoginContext mitzuteilen, dass er dieses Modul ignorieren soll.
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;
// Extrahieren des WSPrincipal und WSCredential aus dem gemeinsamen Status
WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.WSPRINCIPAL_KEY);
WSCredential credential = (WSCredential) sharedState.get(Constants.WSCREDENTIAL_KEY);
if (useWSPrincipalName)
{
// Dieses Beispielzuordnungsmodul ermöglicht den direkten Abruf der ZOS_USERID aus dem
// Namen des WSPrincipal, wenn die Eigenschaft "useWSPrincipalName" auf true gesetzt ist.
if (debugEnabled)
{
debug("Using name from WSPrincipal to obtain ZOS_USERID");
}
name = createName(principal);
String realm = getRealm(credential);
// Für dieses Beispiel wird ein Prüftoken erstellt, das eine Kombination aus der ZOS_USERID,
// dem Realm und dem Namen dieses Zuordnungsmoduls ist.
//
// An Stelle dieses Beispieltoken können Sie aus verfügbaren Daten
// ein eigenes Token erstellen.
audit = realm + "/" + name + " MappingModule:" + MAPPING_MODULE_NAME;
// Ein Subject kann mehr als einen Principal enthalten. Dieser Wert gibt die
// Klasse des Principals an, die zurückgegeben wird, wenn vom Subject der
// Caller Principal angefordert wird. Falls das Subject einen angepassten
// Principal enthält, kann hier der Klassenname angegeben werden.
//
// Für die Caller Principal Class werden zwei Vorgaben bereitgestellt:
//
// - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
// z/OS-Principal-Klasse
//
// - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
// Standard-Principal-Klasse
principalClass = AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS;
succeeded = true;
}
else
{
if (debugEnabled)
{
debug("Using Custom logic to obtain ZOS_USERID");
}
// Falls das Verhalten dieses Zuordnungsmoduls nicht ausreicht, um die ZOS_USERID aus dem
// WSPrincipal-Namen zu erhalten, kann hier eine eigene Zuordnungslogik angegeben werden.
//
// Soll diese angepasste Zuordnungslogik verwendet werden, muss die Eigenschaft "useWSPrincipalName"
// auf false gesetzt werden.
// name = ... eigene Logik
// audit = ... eigene Logik
// principalClass = ... eigene Logik
// Standardmäßig wird keine eigene Zuordnung angegeben, sodass dieser Codepfad
// false ergibt. Bei Angabe einer eigenen Zuordnung sollte die folgende Variable
// modifiziert werden, um Erfolg oder Scheitern der eigenen Zuordnung anzugeben.
succeeded = false;
}
if (succeeded)
{
// Die Werte für name, audit und principalClass liegen vor und müssen nur
// noch in den gemeinsamen Status gestellt werden.
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[] { "Values have been stored into the shared state", name, audit, principalClass });
}
}
}
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 der Authentifizierung (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 eines Subject-Objekts
*
* 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 erfolgreiche 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);
}
}
}
/*
* Private Methode für den Abruf des Realmnamens aus dem Credential und die Rückgabe des Namens. * Die Ausnahmebehandlung bleibt am Abrufen des Realmnamens aus der Hauptlogik von login()
* beteiligt.
*/
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 löst CredentialExpiredException und CredentialDestroyedException aus
if (debugEnabled)
{
debug(new Object[] { "Caught exception in getRealm: ", e });
}
realm = "UNKNOWN_REALM";
}
if (debugEnabled)
{
debug("getRealm() exit");
}
return realm;
}
/*
* Private Methode zum Generieren der ZOS_USERID aus dem WSPrincipal-Namen.
*/
private String createName(WSPrincipal principal)
{
if (debugEnabled)
{
debug("createName() entry");
}
String name = principal.getName();
if (debugEnabled)
{
debug("Using name='" + name + "' from principal");
}
// WSPrincipal.getName() kann REALM/NAME zurückgeben, daher wird die String analysiert, um nur den Namen zu erhalten.
int index = name.indexOf("/") + 1; // Index des ersten Zeichens nach dem ersten /
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 WSPrincipal 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);
}
// 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);
}
}
// MVS-IDs müssen aus Großbuchstaben bestehen.
name = name.toUpperCase();
if (debugEnabled)
{
debug("createName() exit");
}
return name;
}
}
- Kompilieren Sie den Java-Code. Vergewissern Sie sich, dass der Code anerkannt und genauso wie ein APF-autorisiertes Modul behandelt wird. Der z/OS-Controller greift auf die Standard-JAAS-Anmeldekonfiguration (Java Authorization and Authentication Service) zu.
- Falls Sie eine von der IBM® Standardvorgabe abweichende Zuordnungsklasse angeben, müssen Sie die Klasse auf den Anwendungsservern und Deployment Managern im Verzeichnis "classes" installieren. Speichern Sie die JAR-Datei im Verzeichnis WAS_HOME/classes für jeden Knoten in der Zelle, einschließlich des Deployment-Manager-Knotens in einer WebSphere Application Server Network Deployment-Zelle.