All das JAAS

Mussten Sie jemals einen Anmeldeauthentifizierungsmechanismus für eine Anwendung erstellen? Die Chancen stehen gut und wahrscheinlich mehr als einmal, wobei jede neue Implementierung der vorherigen nahe kommt, aber nicht mit dieser identisch ist. Beispielsweise kann eine Implementierung eine Oracle-Datenbank verwenden, eine andere eine NT-Authentifizierung und eine andere ein LDAP-Verzeichnis (Lightweight Access Directory Protocol). Wäre es nicht schön, all diese Sicherheitsmechanismen zu unterstützen, ohne den Code auf Anwendungsebene zu ändern?

Jetzt in der Java-Welt können Sie mit dem Java Authentication and Authorization Service (JAAS). Diese relativ neue API war eine Erweiterung in J2SE (Java 2 Platform, Standard Edition) 1.3, ist eine Kern-API in J2SE 1.4 und Teil der J2EE (Java 2 Platform, Enterprise Edition) 1.3-Spezifikation. In diesem Artikel lernen Sie die Grundlagen von JAAS kennen und zeigen Ihnen, wie Sie JAAS effektiv auf reale Anwendungen anwenden können. Wir haben die Anwendung dieses Artikels auf unseren eigenen Erfahrungen mit der Integration von JAAS in ein vorhandenes webbasiertes Java-System basiert, das ein RDBMS (relationales Datenbankverwaltungssystem) zum Speichern von Benutzeranmeldeinformationen verwendet. Mit JAAS haben wir robustere, flexiblere und konsistentere Anmelde- und Authentifizierungsmechanismen entwickelt.

Sie können einen vollständigen Satz von Arbeitsbeispielen aus den folgenden Ressourcen herunterladen (einschließlich Java-Quellen, JSPs (JavaServer Pages), JAAS-Konfiguration mit Datenbank- und Build-Skripten). Wir haben diese Beispiele mit dem Resin-Server mit JDBC (Java Database Connectivity) und der MySQL-Datenbank getestet.

Java-Authentifizierung und -Autorisierung: Das große Ganze

Vor JAAS war das Sicherheitsmodell von Java hauptsächlich von seiner Entstehung als plattformunabhängige Sprache für verteilte, vernetzte Anwendungen geprägt. In den Anfängen wurde Java häufig als mobiler Code angezeigt, z. B. als browserbasierte Applets. Daher konzentrierte sich das ursprüngliche Sicherheitsmodell auf den Schutz der Benutzer basierend darauf, woher der Code stammt und wer ihn erstellt hat. Frühe Java-Sicherheitsmechanismen wie SecurityManagers, das Sandbox-Konzept, die Codesignatur und Richtliniendateien sollten Benutzer vor dem System schützen.

Die Erfindung von JAAS spiegelt Javas Entwicklung zu einer universellen Programmiersprache wider, die zur Implementierung traditioneller Client- und Serveranwendungen verwendet wird, die Anmeldung und Zugriffskontrolle erfordern. JAAS schützt das System vor Benutzern, indem der Zugriff basierend darauf, wer oder was das Programm ausführt, zugelassen oder verweigert wird . Während JAAS sowohl Authentifizierung als auch Autorisierung durchführen kann, konzentrieren wir uns in diesem Artikel hauptsächlich auf die Authentifizierung.

JAAS kann Ihre Java-Sicherheitsentwicklung vereinfachen, indem eine Abstraktionsschicht zwischen Ihrer Anwendung und unterschiedlichen zugrunde liegenden Authentifizierungs- und Autorisierungsmechanismen eingefügt wird. Diese Unabhängigkeit von Plattformen und Algorithmen ermöglicht es Ihnen, verschiedene Sicherheitsmechanismen zu verwenden, ohne den Code auf Anwendungsebene zu ändern. Wie bei den meisten Java-Sicherheits-APIs erreicht JAAS diese Implementierungsunabhängigkeit durch ein erweiterbares Framework aus steckbaren Service Provider-Schnittstellen (SPIs): eine Reihe abstrakter Klassen und Schnittstellen, für die bestimmte Implementierungen entwickelt werden.

Abbildung 1 unten gibt einen allgemeinen Überblick darüber, wie JAAS diese Steckbarkeit erreicht. Ihr Code auf Anwendungsebene befasst sich hauptsächlich mit a LoginContext. Darunter LoginContextbefindet sich ein Satz von einem oder mehreren dynamisch konfigurierten LoginModules, die die eigentliche Authentifizierung unter Verwendung der entsprechenden Sicherheitsinfrastruktur durchführen.

JAAS bietet einige Referenzimplementierungen LoginModule, wie z JndiLoginModule. Sie können auch Ihre eigenen entwickeln, wie wir es hier mit dem tun RdbmsLoginModule. Wir zeigen auch, wie Sie mithilfe einer einfachen Konfigurationsdatei schnell eine Anwendung mit einer Auswahl von Implementierungen einrichten können.

JAAS ist nicht nur steckbar, sondern auch stapelbar: Im Rahmen einer einzelnen Anmeldung können mehrere Sicherheitsmodule übereinander gestapelt werden, die jeweils nacheinander aufgerufen werden und mit einer anderen Sicherheitsinfrastruktur interagieren.

JAAS-Aspekte basieren auf einigen bekannten Sicherheitsarchitekturmustern und vorhandenen Frameworks. Die stapelbare Funktion ähnelt beispielsweise bewusst dem PAM-Framework (Unix Pluggable Authentication Module). Aus transaktionaler Sicht nimmt JAAS Verhaltensweisen an, die den 2PC-Protokollen (Two-Phase Commit) ähneln. Die Sicherheitskonfigurationskonzepte von JAAS, einschließlich PolicyDateien und Permissions, stammen aus den J2SE 1.2-Sicherheitspaketen. JAAS leiht sich auch Ideen aus anderen etablierten Sicherheits-Frameworks aus, z. B. X.509-Zertifikaten, von denen der Name Subjectabgeleitet ist (mehr dazu Subjectspäter).

Hinweis: JAAS ist nur eine von mehreren neuen Java-Sicherheits-APIs. Weitere Informationen zur Java-Sicherheit finden Sie in der Seitenleiste "Das Java-Sicherheitspuzzle" und in den Ressourcen unten.

Client- und serverseitiges JAAS

Sie können JAAS sowohl auf dem Client als auch auf dem Server anwenden. Die Verwendung auf der Client-Seite ist unkompliziert, wie wir in Kürze demonstrieren werden. Auf der Serverseite werden die Dinge etwas komplexer. Derzeit ist JAAS auf dem Anwendungsservermarkt etwas inkonsistent. J2EE-App-Server verwenden JAAS etwas anders, je nachdem, welchen Sie verwenden. Zum Beispiel integriert JBossSX mithilfe seiner eigenen Architektur JAAS gut in sein Gesamtsicherheitsframework (das in Scott Starks ausgezeichnetem JavaWorld- Artikel "Integrieren von Sicherheitsinfrastrukturen in JBossSX" (August 2001) beschrieben wird). Obwohl WebLogic 6.x JAAS unterstützt, unterscheiden sich die Details.

Damit Sie JAAS sowohl aus server- als auch aus clientseitiger Perspektive verstehen können, werden in diesem Artikel Beispiele für beides vorgestellt. Der Einfachheit halber verwenden wir den Resin-Anwendungsserver, damit wir mit einem saubereren Slate beginnen können (Resin verfügt zwar über ein eigenes steckbares Authentifizierungsschema, ist jedoch nicht standardisiert, sodass die Verwendung von JAAS uns mehr Portabilität bietet Optionen später).

Kern JAAS

Um mit JAAS zu beginnen, müssen Sie zunächst sicherstellen, dass es installiert ist. J2SE 1.4 enthält bereits JAAS; J2SE 1.3 nicht. Wenn Sie J2SE 1.3 weiterhin verwenden möchten, laden Sie JAAS von Sun Microsystems herunter. Sobald Sie JAAS heruntergeladen und in einem bestimmten Verzeichnis installiert haben, wird ein Unterverzeichnis mit dem Namen angezeigt lib, das eine Datei mit dem Namen enthält jaas.jar. Sie müssen diese Datei zu Ihrem Klassenpfad hinzufügen oder in Ihr JRE-Erweiterungsverzeichnis (Java Runtime Environment) kopieren (in \lib\ext, wo sich der Speicherort Ihrer JRE befindet). Sie sind dann JAAS-fähig. Hinweis: Wenn Sie einen Anwendungsserver verwenden, enthält dieser möglicherweise bereits JAAS. Überprüfen Sie die Dokumentation Ihres Servers auf Details.

Beachten Sie, dass Sie bei jedem dieser Ansätze einige der JAAS-bezogenen Systemeigenschaftseinstellungen (sowie viele andere Java-Sicherheitseinstellungen) in der Java-Sicherheitseigenschaftendatei ändern können. Diese Datei java.securitybefindet sich im /lib/securityVerzeichnis und ist im Standarddateiformat für Java-Eigenschaften geschrieben.

Die Verwendung der JAAS-Authentifizierung in Ihrer Anwendung umfasst normalerweise die folgenden Schritte:

  1. Ein ... kreieren LoginContext
  2. Übergeben Sie optional a CallbackHandleran LoginContext, um Authentifizierungsdaten zu sammeln oder zu verarbeiten
  3. Führen Sie die Authentifizierung durch den Aufruf LoginContext‚s login()Methode
  4. Führen Sie privilegierte Aktionen mit dem zurückgegebenen aus Subject(vorausgesetzt, die Anmeldung ist erfolgreich).

Hier ist ein minimales Beispiel:

LoginContext lc = neuer LoginContext ("MyExample"); try {lc.login (); } catch (LoginException) {// Authentifizierung fehlgeschlagen. } // Authentifizierung erfolgreich, wir können jetzt fortfahren. // Wir können den zurückgegebenen Betreff verwenden, wenn wir möchten. Betreff sub = lc.getSubject (); Subject.doAs (sub, new MyPrivilegedAction ());

Unter der Decke treten noch einige andere Dinge auf:

  1. Während der Initialisierung LoginContextfindet der Konfigurationseintrag "MyExample"in einer JAAS-Konfigurationsdatei (die Sie konfiguriert haben), um zu bestimmen, welche LoginModules geladen werden sollen (siehe Abbildung 2).
  2. Während der Anmeldung LoginContextruft jeder LoginModuledie login()Methode auf
  3. Jede login()Methode führt die Authentifizierung durch oder schreibt a einCallbackHandler
  4. Der CallbackHandlerverwendet ein oder mehrere Callbacks, um mit dem Benutzer zu interagieren und Eingaben zu sammeln
  5. A new Subject instance is populated with authentication details such as Principals and credentials

We'll explain further details below, but to begin, let's look at the key JAAS classes and interfaces involved in the process. These are typically divided into the following three groups:

Table 1. JAAS classes and interfaces

Common Subject, Principal, credential (credential is not any specific class, but can be any object)
Authentication LoginContext, LoginModule, CallbackHandler, Callback
Authorization Policy, AuthPermission, PrivateCredentialPermission

Most of these classes and interfaces are in the javax.security.auth package's subpackages, with some prebuilt implementations in the com.sun.security.auth package, included only in J2SE 1.4.

Note: Because we focus on authentication in this article, we don't delve into the authorization classes.

Common: Subjects, Principals, and Credentials

The Subject class represents an authenticated entity: an end-user or administrator, or a Web service, device, or another process. The class contains three sets of security information types:

  • Identities: In the form of one or more Principals
  • Public credentials: Such as name or public keys
  • Private credentials: Like passwords or private keys

Principals represent Subject identities. They implement the java.security.Principal interface (which predates JAAS) and java.io.Serializable. A Subject's most important method is getName(), which returns an identity's string name. Since a Subject instance contains an array of Principals, it can thus have multiple names. Because a social security number, login ID, email address, and so on, can all represent one user, multiple identities prove common in the real world.

The last element here, credential, is not a class or an interface, but can be any object. Credentials can include any authentication artifact, such as a ticket, key, or password, that specific security systems might require. The Subject class maintains unique Sets of private and public credentials, which can be retrieved with methods such as getPrivateCredentials() and getPublicCrendentials(). These methods are more often used by security subsystems than at the application layer.

Authentication: LoginContext

Your application layer uses LoginContext as its primary class for authenticating Subjects. LoginContext also represents where JAAS's dynamic pluggability comes into play, because when you construct a LoginContext, you specify a named configuration to load. The LoginContext typically loads the configuration information from a text file, which in turn tells the LoginContext which LoginModules to use during login.

The three commonly used methods in LoginContext are:

Table 2. LoginContext methods

login() Performs login, a relatively complex step that invokes all LoginModules specified for this configuration. If it succeeds, it creates an authenticated Subject. If it fails, it throws a LoginException.
getSubject() Returns the authenticated Subject.
logout() Logs out the authenticated Subject and removes its Principals and credentials.

We will show how to use these methods later.

Authentication: LoginModule

LoginModule is the interface to specific authentication mechanisms. J2SE 1.4 ships with a set of ready-to-use LoginModules, including:

Table 3. LoginModules in J2SE 1.4

JndiLoginModule Verifies against a directory service configured under JNDI (Java Naming and Directory Interface)
Krb5LoginModule Authenticates using Kerberos protocols
NTLoginModule Uses the current user's NT security information to authenticate
UnixLoginModule Uses the current user's Unix security information to authenticate

Along with these modules comes a set of corresponding concrete Principal implementations in the com.sun.security.auth package, such as NTDomainPrincipal and UnixPrincipal.

The LoginModule interface has five methods:

Table 4. LoginModule methods

initialize() Called after the LoginModule is constructed.
login() Performs the authentication.
commit() Called by the LoginContext after it has accepted the results from all LoginModules defined for this application. We assign Principals and credentials to the Subject here.
abort() Called when any LoginModule for this application fails (even though earlier ones in sequence may have succeeded—thus akin to a 2PC model). No Principals or credentials are assigned to the Subject.
logout() Removes the Principals and credentials associated with the Subject.

The application layer calls none of these methods directly—the LoginContext invokes them as needed. Our example below will elaborate on these methods' implementations.

Authentication: CallbackHandlers and Callbacks

CallbackHandlerMit s und Callbacks kann LoginModuleein Benutzer die erforderlichen Authentifizierungsinformationen von einem Benutzer oder System sammeln und dabei unabhängig vom tatsächlichen Interaktionsmechanismus bleiben. Wir werden diese Fähigkeit in unserem Design nutzen - unsere RdbmsLoginModulehängt nicht davon ab, wie die Benutzeranmeldeinformationen (Benutzername / Passwort) abgerufen werden, und kann daher in den verschiedenen Anwendungsumgebungen verwendet werden, die wir veranschaulichen werden (entweder über die Befehlszeile oder über eine JSP). .