Java Authentication and Authorization Service, kurz JAAS, ist in der Java-Welt das Standardvorgehen zur Authentifizierung und Autorisierung in Java-Anwendungen. Das Eclipse JAAS – noch ein Incubator Projekt – will die Lücke zwischen JAAS Standard und Eclipse PDE schliessen.
Zur Authentifizierung bietet Eclipse JAAS verschiedene Extensionpoints, die es ermöglichen, die Bestandteile von JAAS in der plugin.xml vorzunehmen. Bestandteil dieses Beitrags ist aber zunächst die Authentifizierung in einer Eclipse RAP Anwendung mittels JAAS.
org.eclipse.equinox.security.loginModule
Der loginModule Extensionpoint ermöglicht die Registrierung eines JAAS LoginModule.
<extension id="myLoginModule"
name="My Login Module"
point="org.eclipse.equinox.security.loginModule">
<loginModule
class="org.foo.bar.MyLoginModule"
description="My Login Module">
</loginModule>
</extension>
<extension id="unixLoginModule"
name="Unix Login Module"
point="org.eclipse.equinox.security.loginModule">
<loginModule
<!-- part of standard jdk -->
class="com.sun.security.auth.module.UnixLoginModule"
description="Unix Login Module">
</loginModule>
</extension>
org.eclipse.equinox.security.callbackHandlerMapping
Mit diesem Extensionpoint wird das Mapping zwischen eine JAAS Loginkonfiguration und einem konkreten CallbackHandler definitiert.
<extension point="org.eclipse.equinox.security.callbackHandlerMapping">
<callbackHandlerMapping
callbackHandlerId="org.foo.bar.basicAuthDialog"
configName="MyLogin">
</callbackHandlerMapping>
</extension>
org.eclipse.equinox.security.loginConfigurationProvider
<extension id="myLoginConfigurationProvider"
point="org.eclipse.equinox.security.loginConfigurationProvider">
<loginConfigurationProvider
class="org.foo.bar.MyLoginConfigProvider"/>
</extension>
org.eclipse.equinox.security.callbackHandler
Der CallbackHandler ermöglicht Nutzerinteraktionen, zb. zur Eingabe des Logins und Passworts im Dialog oder auf der Kommandozeile.
<extension id="basicAuthDialog"
name="Basic Authentication Dialog"
point="org.eclipse.equinox.security.callbackHandler">
<callbackHandler
class="org.foo.bar.BasicAuthHandler">
</callbackHandler>
</extension>
Im Eclipse RAP Projekt können die meisten Ansätze aus RCP übernommen werden. Das CVS (dev.eclipse.org) liefert auch eine RCP-Beispielanwendung mit, die als Basis für das RAP-Projekt dienen kann.
public class BasicAuthHandler extends AbstractLoginDialog {
private String username;
private char[] password;
protected Control createDialogArea(Composite parent) {
// implement user interface
}
public void internalHandle() {
Callback[] callbacks = getCallbacks();
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
((NameCallback) callbacks[i]).setName(username);
} else if (callbacks[i] instanceof PasswordCallback) {
((PasswordCallback) callbacks[i]).setPassword(password);
}
}
}
}
Die wichtigsten Ausschnitte des abstrakten Logindialogs mit Implementierung des CallbackHandler Interface:
public abstract class AbstractLoginDialog extends TitleAreaDialog
implements CallbackHandler {
Callback[] callbackArray;
protected final Callback[] getCallbacks() {
return this.callbackArray;
}
public abstract void internalHandle();
public void handle(final Callback[] callbacks) throws IOException {
this.callbackArray = callbacks;
final Display display = Display.getDefault();
display.syncExec(new Runnable() {
public void run() {
isCancelled = false;
setBlockOnOpen(false);
open();
final Button okButton = getButton(IDialogConstants.OK_ID);
okButton.setText("Login");
okButton.addSelectionListener(new SelectionListener() {
public void widgetSelected(final SelectionEvent event) {
processCallbacks = true;
}
public void widgetDefaultSelected(final SelectionEvent event) {
// nothing to do
}
});
final Button cancel = getButton(IDialogConstants.CANCEL_ID);
cancel.addSelectionListener(new SelectionListener() {
public void widgetSelected(final SelectionEvent event) {
isCancelled = true;
processCallbacks = true;
}
public void widgetDefaultSelected(final SelectionEvent event) {
// nothing to do
}
});
}
});
try {
ModalContext.setAllowReadAndDispatch(true);
ModalContext.run(new IRunnableWithProgress() {
public void run(final IProgressMonitor monitor) {
// Wait here until OK or cancel is pressed, then let it rip.
// The event listener is responsible for closing the dialog
// (in the loginSucceeded event).
while (!processCallbacks) {
try {
Thread.sleep(100);
} catch (final Exception e) {
// do nothing
}
}
processCallbacks = false;
// Call the adapter to handle the callbacks
if (!isCancelled()) {
internalHandle();
}
}, true, new NullProgressMonitor(), Display.getDefault());
} catch (final Exception e) {
final IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
}
...
}
Beispielanwendung
Eine Beispielanwendung, die vor dem Laden und Starten der Workbench ein Login sicher stellt, könnte zb. so aussehen:
package org.foo.bar;
public class Application implements IEntryPoint {
public int createUI() {
Display display = PlatformUI.createDisplay();
if (login(display)) {
WorkbenchAdvisor advisor = new ApplicationWorkbenchAdvisor();
return PlatformUI.createAndRunWorkbench(display, advisor);
}
return 0;
}
private boolean login(final Display display) {
String configName = "MyLogin";
URL configUrl = Activator.getDefault().getBundle()
.getEntry("data/jaas_config.txt");
ILoginContext secureContext = LoginContextFactory
.createContext(configName, configUrl);
Subject s = null;
do {
try {
s = secureContext.getSubject();
} catch (LoginException e) {
// handle login exception
}
} while (s == null);
return true;
}
}
Und schliesslich die zugehörige JAAS Loginkonfiguration in data/jaas_config.txt.
MyLogin {
org.eclipse.equinox.security.auth.module.ExtensionLoginModule required
extensionId="org.foo.bar.unixLoginModule";
org.eclipse.equinox.security.auth.module.ExtensionLoginModule required
extensionId="org.foo.bar.myLoginModule"
};