[ Team LiB ] |
Introduction to JAASAs 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 FileAccording 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:
Callback HandlersJAAS 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:
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 Informationimport 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 LoginContextTo 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 } UsernamePasswordLoginModuleWebLogic 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() MethodAfter 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 PrivilegedExceptionActionPrivilegedAction 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 Methodsimport 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 CodeWe 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 ] |