[ Team LiB ] Previous Section Next Section

Introduction to JAAS

As discussed in the last chapter, JAAS introduces the following classes: Subject, Principal, LoginContext, and LoginModule. We'll now see how to uses these classes and others to authenticate WebLogic Server application clients.

JAAS Login Configuration File

According to J2SE specifications, to keep JAAS login implementations separate from application code, a login configuration file is used at runtime to determine which LoginModules should be used for client authentication. A login configuration file contains one or entries of the following form:


<entry name> {
<LoginModule> <flag> <LoginModule options>;
<LoginModule> <flag> <LoginModule options>;
    . . .
    };

The entry name, used in application code, is associated with one or more LoginModules at runtime. An example of this is


Sample {
weblogic.security.auth.login.UsernamePasswordLoginModule required debug=false;
};

You can specify the location and name of the JAAS login configuration file using either the command line or the Java security properties file:

  • Command line— Set the system property when starting the JVM like the following example:

    
    
    Djava.security.auth.login.config==file:D:/jaas.conf
    
  • Java security properties file— One or more configuration files can be specified with the following format:

    
    
    login.config.url.XXX=file:D:/jaas.conf
    

    where XXX starts at 1 followed by 2, and so on. The Java security properties file is located in the JAVA_HOME\jre\lib directory and is called java.security.

Callback Handlers

JAAS employs a flexible model for gathering client credentials whether they are username and password pairs or another form. Callback handlers are sent to LoginContexts and LoginModules. These are functions that call into the application client itself. For instance, if the username isn't supplied, a LoginModule will call an appropriate callback and the application client can decide how to respond. It might present the end user with a window or collect the information from a swiped smart card. Callbacks do not impose any restrictions on how application clients can gather their client credentials.

Applications implement the CallbackHandler interface found in the javax.security.auth.callback package. This interface contains a single method:


public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException

When a LoginModule needs client credentials, it invokes the CallbackHandler and passes it information in the form of an array of callbacks. Each of the following classes implements the Callback interface specified in the javax.security.auth.callback package:

  • NameCallback— Used to request username

  • URLCallback— Used to request URL

  • PasswordCallback— Used to request password

  • TextInputCallback— Used to request generic text information

  • TextOutputCallback— Used to provide information, warnings, or error messages

How a callback handler responds to these callbacks is completely application specific. Listing 28.2 is an example of a CallbackHandler that uses the JOptionPane Swing class to display a dialog box to supply a missing username, password, or URL.

Listing 28.2 A CallbackHandler That Displays Swing Dialog Boxes to Request Missing Information
import java.io.*;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.NameCallback;
import weblogic.security.auth.callback.URLCallback;

import javax.swing.*;
class SwingCallbackHandler implements CallbackHandler
{
private String username = null;
private String password = null;
private String url = null;

public SwingCallbackHandler() { }
public SwingCallbackHandler(String pUsername, String pPassword, String pUrl)
  {
username = pUsername;
password = pPassword;
url = pUrl;
  }

public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException
  {
for(int i = 0; i < callbacks.length; i++)
    {
if(callbacks[i] instanceof TextOutputCallback)
      {
// Display the message according to the specified type
TextOutputCallback toc = (TextOutputCallback)callbacks[i];
switch(toc.getMessageType())
        {
case TextOutputCallback.INFORMATION:
System.out.println(toc.getMessage());
break;
case TextOutputCallback.ERROR:
System.out.println("ERROR: " + toc.getMessage());
break;
case TextOutputCallback.WARNING:
System.out.println("WARNING: " + toc.getMessage());
break;
default:
throw new IOException("Unsupported message type: "
          toc.getMessageType());
        }
      }
else if(callbacks[i] instanceof NameCallback)
      {
// Display dialog box for name if not present
NameCallback nc = (NameCallback)callbacks[i];
if (username == null || username.length() == 0) {
String s = JOptionPane.showInputDialog("Please enter your name");
if (s == null)
s = "";
nc.setName(s);

        }
else {
nc.setName(username);
        }
      }
else if(callbacks[i] instanceof URLCallback)
      {
// Display dialog box for url if not present
URLCallback uc = (URLCallback)callbacks[i];
if (url == null || url.length() == 0) {
String s = JOptionPane.showInputDialog("Please enter URL");
if (s == null)
s = "";
uc.setURL(s);
        }
else {
uc.setURL(url);
        }
      }
else if(callbacks[i] instanceof PasswordCallback)
      {
PasswordCallback pc = (PasswordCallback)callbacks[i];
// Display dialog box for password if not present
if (password == null || password.length() == 0) {
// Note: JAAS specifies that the
// password is a char[] rather than a String
String s = JOptionPane.showInputDialog("Please enter password");
if (s == null)
s = "";
int passLen = s.length();
char[] passwordArray = new char[passLen];
for(int passIdx = 0; passIdx < passLen; passIdx++)
passwordArray[passIdx] = s.charAt(passIdx);
pc.setPassword(passwordArray);
        }
else {
pc.setPassword(password.toCharArray());
        }
      }
else
      {
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
      }
    }
  }
}

As you can see, the constructor for SimpleCallbackHandler takes a username, password, and a URL. It responds to the following callbacks: TextOutputCallback, NameCallback, URLCallback, PasswordCallback, and TextInputCallback. In the case of NameCallback, URLCallback, PasswordCallback, and TextInputCallback, if information isn't present, SimpleCallbackHandler prompts the end user at the command line, reads the user's input, and calls the appropriate method in the callback. In the case of TextOutputCallback, it logs the information.

Instantiating LoginContext

To start things off, application clients will instantiate a new instance of LoginContext. The LoginContext class, found in the javax.security.auth.login package, has four different signatures for its constructor. In this version, we're passing an index into the JAAS login configuration file and a callback handler. The index was discussed in the "JAAS Login Configuration File" section. This code should be wrapped in a try/catch block in case the entry name isn't found in the JAAS login configuration file or if other errors occur.


try {
loginContext = new LoginContext("UserPswd",
new MyCallbackHandler(username, password));
} catch(SecurityException se) {
// respond to exception
} catch(LoginException le) {
// respond to exception
}

UsernamePasswordLoginModule

WebLogic Server ships with a LoginModule suitable for application clients called UsernamePasswordLoginModule. It's included with an application client and executes on the client. It requires three parameters: username, password, and the URL of the WebLogic Server instance to perform authentication against. To use UsernamePasswordLoginModule in your application clients, place a reference to it in the JAAS login configuration file.

Calling the login() Method

After a successful instantiation, the login() method should be called. This method should also be in a try/catch block because it can throw a LoginException derived exception.


try {
loginContext.login();
} catch(FailedLoginException le) {
// respond to exception
} catch(AccountExpiredException ae) {
// respond to exception
} catch(CredentialExpiredException ce) {
// respond to exception
} catch(Exception e) {
// respond to exception
}

After a successful login, we can request a populated Subject object from the LoginContext.


Subject subject = loginContext.getSubject();

PrivilegedAction and PrivilegedExceptionAction

PrivilegedAction is an interface found in the java.security package and is used to execute code that requires user authentication and authorization. To run privileged code, create a class that implements the PrivilegedAction interface. This interface only has one method:


public Object run()

The return object is application dependent and represents the results of the privileged code. If the privileged code might throw an exception, implement the PrivilegedExceptionAction interface instead. This interface only has one method:


public Object run() throws Exception

Listing 28.3 is an example of a PrivilegedAction.

Listing 28.3 A PrivilegedAction That Calls on the EJB Methods
import java.security.PrivilegedAction;
import java.util.Hashtable;

import javax.rmi.PortableRemoteObject;
import javax.naming.*;

import examples.ejb20.basic.beanManaged.AccountHome;
import examples.ejb20.basic.beanManaged.Account;

import java.util.*;
public class JaasAction implements PrivilegedAction
{
private static final String JNDI_NAME = "ejb20-beanManaged-AccountHome";
private String url;
public JaasAction(String url)
  {
this.url = url;
  }

public Object run()
  {
try {
// look up AccountHome
Hashtable ht = new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, "t3://localhost:7001");
InitialContext ic = new InitialContext(ht);
Object obj = ic.lookup(JNDI_NAME);
AccountHome home =
(AccountHome)PortableRemoteObject.narrow(obj, AccountHome.class);
// create new Account
Account account = home.create("123456", 10000);
// find and display all Account with balances > $1500
Collection coll = home.findBigAccounts(1500);
Iterator iter = coll.iterator();
while(iter.hasNext()) {
Account temp = (Account)iter.next();
System.out.println(temp.getPrimaryKey() + ":" + temp.balance());
        }

// remove Account just created
account.remove();

} catch(Exception e) {
e.printStackTrace();
    }

return null;
  }
}

Executing the Code

We can now instantiate the class that implements PrivilegedAction or PrivilegedExceptionAction class and invoke its run method. However, we must associate the run method with the Subject object we've extracted from our LoginContext. The Security class, found in the weblogic.security package, contains a mechanism for this. The Security class contains two methods for this purpose: one for PrivilegedAction and one for PrivilegedExceptionAction.


public static Object runAs(javax.security.auth.Subject user,
java.security.PrivilegedAction action)
throws java.lang.IllegalArgumentException
public static Object runAs(javax.security.auth.Subject user,
java.security.PrivilegedExceptionAction action)
throws java.security.PrivilegedActionException,java.lang.IllegalArgumentException

The next two lines of code demonstrate how to instantiate the PrivilegedAction class and execute its run method:


MyPrivilegedAction myAction = new MyPrivilegedAction();
Security.runAs(subject, myAction);

An alternative is to use an anonymous inner class as shown here:


Security.runAs(subject, new PrivilegedAction() {
public Object run() {
// run privileged code
  }
);
    [ Team LiB ] Previous Section Next Section