< Day Day Up > |
Authentication is the process by which the identity of a subject is verified . Authentication must be performed in a secure fashion; otherwise , a malicious entity may impersonate legitimate entities to gain access to a system. Unlike the Object Management Group's CORBA and Microsoft's Component Object Model (COM) and Distributed Component Object Model (DCOM), JAAS authentication and authorization are not performed every time a Java method is called. The principal-based Java authentication and authorization processes are integrated with the previously existing J2SE authentication and authorization mechanisms. Authentication and authorization routines must be called explicitly. An important feature of JAAS is that it can be configured to support a wide variety of authentication mechanisms. For example, the authentication mechanism could require
9.2.1 Pluggable Authentication via LoginModule sAn authenticator is a security component responsible for authenticating the identity of a Subject . In JAAS, authenticators are Java classes implementing the LoginModule interface in package javax.security.auth.spi . LoginModule s are supplied by authentication technology providers. As one of the goals of JAAS is to have a pluggable authentication mechanism, the LoginModule framework methods are generic enough to allow all authentication mechanisms to work and simple enough so that complexity need not be a hindrance to authentication mechanism providers. Each LoginModule must implement the four authentication methods in the javax.security.auth.spi.LoginModule API: login() , commit() , abort() , and logout() . As shown in Listing 9.1, these methods have no parameters, and a Java return type of boolean ” true if the method succeeded or false if the LoginModule should be ignored. If the method failed, a javax.Security.auth.login.LoginException will be thrown. Listing 9.1. The LoginModule Interfacepublic interface LoginModule { void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options); boolean login() throws LoginException; boolean commit() throws LoginException; boolean abort() throws LoginException; boolean logout() throws LoginException; } This approach certainly succeeds in not requiring authentication providers to add constraints to their current interfaces but still leaves the issue of how to provide additional configuration information when required. Additional information is supplied to the LoginModule via the initialize() method, which takes four arguments.
The kind of proof necessary for authentication depends on the security requirements of a particular resource and the enterprise security policies. To provide such flexibility, the JAAS authentication framework is designed around the concept of configurable authenticators . This architecture allows system administrators to configure, or plug in , the authenticators appropriate for the security requirements of the deployed application. This configuration is typically specified on the command line when the application is started or can be specified with a call to System.setProperty() . The configuration location can also be specified in the form of a URL in the security properties file in the lib/security directory. The JAAS architecture also enables applications to remain independent of underlying authentication mechanisms. Therefore, as new authenticators become available or as current authentication services are updated, system administrators can easily replace or add authenticators without having to modify or recompile existing applications. This allows greater portability of applications, as the authentication implementations are not hard-wired into the applications. Porting an application to a new platform does not require rewriting the application. Instead, the JAAS LoginModule configuration file is modified to specify the authenticators for the platform, and the LoginContext class in package javax.security.auth.login handles the rest of the details of calling the configured authenticators. No application coding changes are required. The LoginContext class represents a Java implementation of the plug-in framework. The LoginContext consults the LoginModule configuration file to determine the authenticators, or LoginModule s, to be used by an application. As we see in Section 9.2.2 on page 296, the first argument to the LoginContext constructor is the name of the stanza , which describes the set of LoginModule s to be used, and the second argument is a CallbackHandler instance. JAAS also supports the notion of stacked authenticators . This means that an application may be configured to use more than one LoginModule . For example, one could configure both a Kerberos LoginModule and a smart-card LoginModule for an application. The JAAS authentication framework ensures that either all the configured LoginModule s succeed or none succeed via a two-phase process. It is the responsibility of the LoginContext performing the authentication to ensure this all-or-nothing behavior.
In general, a LoginModule in a LoginModule configuration file stanza has one of the following four attributes:
The overall authentication succeeds only if all required and requisite LoginModule s succeed. If a sufficient LoginModule is configured and succeeds, only the required and requisite LoginModule s prior to that sufficient LoginModule need to have succeeded for the overall authentication to succeed. If no required or requisite LoginModule s are configured for an application, at least one sufficient or optional LoginModule must succeed. A stanza with a single or stand-alone LoginModule exhibits identical behavior, regardless of the attribute associated with it. In addition, it is possible to specify a space-separated list of LoginModule -specific option keys and values that are passed directly to the underlying LoginModule s. Option keys and values are defined by the LoginModule itself. The options control the behavior of the LoginModule instance. For example, a LoginModule may define options to support debugging and testing capabilities. There is no limit to the number of options a LoginModule may define. 9.2.2 JAAS LoginModule ExamplesWe examine the richness of the login process through a series of three LoginModule s (Section 9.2.2.1 on page 301, Section 9.2.2.2 on page 305, and Section 9.2.2.3 on page 308). But first, we provide a sample Main program that shows the context in which LoginModule s are used (Listing 9.2). Listing 9.2. Main.javapackage jaasexample; import java.security.Principal; import java.util.Iterator; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; /** * Sample application used to demonstrate how to use JAAS * LoginModules. */ public class Main { /** * The main method of this application. * * @param args an array of Strings to be passed on the * command line. The first argument tells the * LoginContext which LoginModule stanza to use * when selecting LoginModules. */ public static void main(String[] args) { (new Main()).run(args); } /** * This method performs the actual LoginModule * demonstration. * * @param args an array of Strings to be passed on the * command line. The first argument tells the * LoginContext which LoginModule stanza to use * when selecting LoginModules. */ public void run(String[] args) { boolean succeeded; // did the login succeed? LoginContext loginContext = null; try { // the first argument tells the LoginContext which // LoginModule stanza to use when selecting // LoginModules. The second argument is the // CallBackHandler to be used by all of the // LoginModules. loginContext = new LoginContext (args[0], new DemoCallbackHandler()); loginContext.login(); // attempt login(s) succeeded = true; // login(s) succeeded } catch (LoginException le) { succeeded = false; // login(s) failed. } System.out.println("Login succeeded? " + succeeded); if (! succeeded) return; // The LoginContext created a Subject. The // LoginModules populated it with Principals and // credentials. Subject subject = loginContext.getSubject(); showPrincipals(subject.getPrincipals()); showCredentials("Public credentials:", subject.getPublicCredentials()); showCredentials("Private credentials:", subject.getPrivateCredentials()); // The next line performs some action that requires a // Subject. doSomethingInteresting(subject); // Attempt to perform a logout on all of the // LoginModules try { loginContext.logout(); } catch (LoginException le) { le.printStackTrace(); } } /** * Subclasses can override and do something interesting * here that requires a Subject. * The default implementation does nothing. * * @param subject is the authenticated Subject. */ protected void doSomethingInteresting(Subject subject) { // do something with the Subject } /** * Show all of the Principals, and the classes that * implement the Principals, contained in the Set passed * as an argument. * * @param principals is a Set of Principals to display. */ public static void showPrincipals(Set principals) { System.out.println("Principals"); Iterator principalsIter = principals.iterator(); if (! principalsIter.hasNext()) System.out.println(" none."); while (principalsIter.hasNext()) { Principal principal = (Principal) principalsIter.next(); System.out.println("Principal: " + principal.getName()); System.out.println(" Class: " + principal.getClass().getName()); } } /** * Show all of the credentials, and the classes that * implement the credentials, contained in the Set * passed as an argument. * * @param title a String to display before printing the * credentials. * @param credentials is a Set of credentials to * display. */ public static void showCredentials(String title, Set credentials) { System.out.println(title); Iterator credIter = credentials.iterator(); if (! credIter.hasNext()) System.out.println(" none."); while (credIter.hasNext()) { Object credential = credIter.next(); System.out.println(" Value: " + credential); System.out.println(" Class: " + credential.getClass().getName()); } } } To enable flexibility in selecting an appropriate set of LoginModule s, a java.lang.String is passed to the LoginContext constructor. In this example, the String is obtained as the command-line argument to the Main application and is applied as an index into the LoginModule configuration file to select the stanza describing the appropriate LoginModule (s) to be used for authentication. By using a configuration file and an indexing mechanism, it is possible to change the stanza, or the set of selected LoginModule s, without requiring modification of the application. This flexibility is demonstrated in the three scenarios in this section. The beauty of this approach is that the LoginModule s can be platform specific, but the application code requesting the authentication can remain platform neutral and can be upgraded without requiring changes to the application. The missing pieces of the Main application are the details of the CallbackHandler instantiated in Main. DemoCallbackHandler , shown in Listing 9.3, is an implementation of a CallbackHandler , providing the function sufficient to demonstrate the LoginModule examples in this chapter. Listing 9.3. DemoCallbackHandler.javapackage jaasexample; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback. UnsupportedCallbackException; /** * A minimal CallbackHandler class that will process * NameCallback and PasswordCallback objects. */ class DemoCallbackHandler implements CallbackHandler { /** * Process the Callback objects passed to the * CallbackHandler, which then calls this method when * CallbackHandler.handle() is called. * * @param callbacks is the array of Callback objects to * be processed. */ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof NameCallback) { // Prompt the user for a user ID NameCallback nameCallback = (NameCallback) callbacks[i]; System.err.print(nameCallback.getPrompt()); System.err.flush(); nameCallback.setName((new BufferedReader (new InputStreamReader(System.in))). readLine()); } else if (callbacks[i] instanceof PasswordCallback) { // Prompt the user for sensitive // information PasswordCallback pwCallback = (PasswordCallback) callbacks[i]; System.err.print(pwCallback.getPrompt()); System.err.flush(); pwCallback.setPassword((new BufferedReader (new InputStreamReader(System.in))). readLine().toCharArray()); } else throw new UnsupportedCallbackException (callbacks[i], "Unrecognized Callback"); } } } 9.2.2.1 First ScenarioThe first step in the authentication process is to create a LoginContext , passing in the name of a LoginModule configuration file stanza and a CallbackHandler . The LoginContext reads the LoginModule configuration file and locates the configuration stanza corresponding to the first argument to the LoginContext 's constructor. The LoginContext , then, instantiates all the LoginModule s specified in the configuration file stanza. Listing 9.4 shows a simple LoginModule configuration file stanza, called Demo1. Note the location of the semicolons. Semicolons terminate LoginModule entries and stanzas. Listing 9.4. Demo1 LoginModule Configuration File StanzaDemo1 { jaasexample.Demo1LoginModule required debug=true succeeded=true; }; When Main.main() calls loginContext.login() , the LoginContext finds the Demo1 stanza and instantiates Demo1LoginModule , specifying that this is required to succeed for the LoginContext 's login() method to succeed. Two options, debug and succeed , with their values, are defined for the Demo1LoginModule . The meaning of these options and their value is LoginModule specific. The option names and their values are passed to the LoginModule via the options argument of the initialize() method. Listing 9.5 gives the code for Demo1LoginModule . Listing 9.5. Demo1LoginModule.javapackage jaasexample; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; /** * A basic LoginModule that records the arguments passed to * the initialize() method, prints debug trace messages, and * determines whether the methods should succeed. * Other than initialize(), other methods return default * values. */ public class Demo1LoginModule implements LoginModule { // To fill in Principals & credentials Subject subject; // To acquire challenge/response info CallbackHandler callbackHandler; // State shared with other LoginModules Map sharedState; // Key/value options from config file stanza for this // LoginModule instance Map options; // config file debug option value boolean debug = false; // config file succeeded option value boolean succeeded = false; /** * Initialize this LoginModule; in particular, record the * <code>debug</code> and <code>succeeded</code> options * for this LoginModule instance as specified in the * LoginModule configuration file. If not specified in * the configuration file, <code>debug</code> will be * false, and <code>succeeded</code> will be true. * * @param subject is the Subject to be constructed by the * LoginModules associated with the LoginContext * that instantiated this LoginModule. * @param callbackHandler is a CallbackHandler that * processes the Callback objects for challenge/ * response. * @param sharedState is a Map of state shared between * LoginModules associated with the LoginContext * that instantiated this LoginModule. * @param options is a Map that defines the options * in the LoginContext configuration file for this * LoginModule. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { // Save the arguments for later use this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; // Get the debug option specified in the // configuration file. String debugValue = (String) options.get("debug"); debug = "true".equalsIgnoreCase(debugValue); // See if the succeeded option was specified in the // configuration file. String succeededValue = (String) options.get("succeeded"); succeeded = ! "false".equalsIgnoreCase(succeededValue); if (debug) System.out.println("Demo1LoginModule.initialize"); } /** * Default login routine that returns the * <code>succeeded</code> value. If <code>debug</code> is * <code>true</code>, a progress message is printed. * * @return the <code>succeeded</code> value. */ public boolean login() throws LoginException { if (debug) System.out.println("Demo1LoginModule.login: " + succeeded); return succeeded; } /** * Default logout routine always returns * <code>true</code>. If <code>debug</code> is * <code>true</code>, a progress message is printed. * * @return the boolean <code>true</code>. */ public boolean commit() throws LoginException { if (debug) System.out.println("Demo1LoginModule.commit"); return true; } /** * Default abort routine that returns the * <code>succeeded</code> value. If <code>debug</code> is * <code>true</code>, a progress message is printed. * * @return the boolean <code>true</code>. */ public boolean abort() throws LoginException { if (debug) System.out.println("Demo1LoginModule.abort: " + succeeded); return true; // return true if the abort has succeeded } /** * Default logout routine always returns * <code>true</code>. If <code>debug</code> is * <code>true</code>, a progress message is printed. * * @return the boolean <code>true</code>. */ public boolean logout() throws LoginException { if (debug) System.out.println("Demo1LoginModule.logout"); return true; } } The initialize() method saves state information, such as the debug and succeed options. As is shown in Listing 9.6, when debug and succeed are both set to true , the Demo1LoginModule.login() method succeeds, and the progress through the Demo1LoginModule 's methods is written to System.out , including the eventual call to the logout() when Main.main() calls loginContext.logout() . Listing 9.6. Running Main with Demo1LoginModulejava -Djava.security.auth.login.config==jaasexample/ jaasexample.config jaasexample.Main Demo1 Demo1LoginModule.initialize Demo1LoginModule.login: true Demo1LoginModule.commit Login succeeded? true Principals none. Public credentials: none. Private credentials: none. Demo1LoginModule.logout Although not shown here, if the succeed option is changed to false , the login() fails, and an Exception is thrown. Note that this scenario's LoginModule configuration file, jaasexample/jaasexample.config , is specified on the command line as the value of the java.security.auth.login.config system property. Alternatively, the value can be set via a call to java.lang.System.setProperty() or in the configuration specified in the security properties file, java.security , in the lib/security directory. 9.2.2.2 Second ScenarioThe second scenario builds on the first scenario. The LoginModule of this example, Demo2LoginModule , extends from Demo1LoginModule . The Demo2 LoginModule stanza is shown in Listing 9.7. Listing 9.7. Demo2 LoginModule Configuration File StanzaDemo2 { jaasexample.Demo2LoginModule required debug=false; }; The source code for Demo2LoginModule is shown in Listing 9.8. Listing 9.8. Demo2LoginModule.javapackage jaasexample; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback. UnsupportedCallbackException; import javax.security.auth.login.LoginException; import javax.security.auth.login.FailedLoginException; /** * Demonstrate a login() method that uses Callback objects to * perform a trivial challenge/response (user ID/password) * and validate them using hard-coded values. * All other methods are inherited from Demo1LoginModule. */ public class Demo2LoginModule extends Demo1LoginModule { String userName = null;// the authenticated user name // hard-coded user ID and password for validation private String referenceName = "demo"; private char[] referencePw = {'d', 'e', 'm', 'o'}; /** * Using Callback objects and the CallbackHandler, prompt * the user for a user ID and password and see if they * match the hard-coded values found in this class. * * @return the boolean <code>true</code> if the * authentication succeeds. * @throws LoginException if authentication fails. */ public boolean login() throws LoginException { super.login(); // Optionally print progress message // the default is that authentication failed succeeded = false; if (callbackHandler == null) throw new LoginException("No CallbackHandler " + "to get info from the user"); // Prompt for user ID and password via // the CallbackHandler passed to initialize() Callback[] callbacks = new Callback[2]; NameCallback nameCallback = new NameCallback("Name: "); callbacks[0] = nameCallback; PasswordCallback passwordCallback = new PasswordCallback("Password: ", false); callbacks[1] = passwordCallback; String name = null; char[] password = null; try { // Prompt for user ID and password callbackHandler.handle(callbacks); } catch (java.io.IOException ioe) { // Login failed throw new LoginException(ioe.toString()); } catch (UnsupportedCallbackException uce) { // Login failed throw new LoginException (uce.getCallback().toString()); } // Retrieve the user ID and password from the // respective Callback objects. name = nameCallback.getName(); char[] tmpPwd = passwordCallback.getPassword(); // treat a null as an empty (length 0) password if (tmpPwd == null) tmpPwd = new char[0]; password = new char[tmpPwd.length]; System.arraycopy (tmpPwd, 0, password, 0, tmpPwd.length); passwordCallback.clearPassword(); // check the user ID boolean nameOk = name.equals(referenceName); if (! nameOk) { password = clearPassword( password ); throw new FailedLoginException("Incorrect name"); } // Trivial password check if (password.length != referencePw.length) { password = clearPassword(password); throw new FailedLoginException ("Incorrect password"); } // Done with the password, so clear it. password = clearPassword(password); succeeded = true; userName = name; return succeeded; } /** * Wipe out the password character array and return a * null character array. * * @param password is the char[] to be cleared. * @return a null character array. */ private char[] clearPassword(char[] password) { for (int i = 0; i < password.length; i++) password[i] = ' '; return null; } } The primary difference with Demo1LoginModule is that the login() method now performs the authentication, and the CallbackHandler is used to prompt the user for a user ID and password and see whether they match the values hard-coded in the class. In login() , a NameCallback and a PasswordCallback are instantiated. These objects are processed via the CallbackHandler 's handle() method. Then, the login() method performs a simple authorization test by comparing the user ID and password from the NameCallback and PasswordCallback objects with hard-coded values in the class. It is easy to extend this class to call a user ID registry to perform the authentication. In the remainder of login() , if the user ID or password do not match, a javax.security.auth.login.FailedLoginException is thrown. In all cases, the password is cleared from memory by calling clearPassword() to put blank characters into the password array. The output from running Main with Demo2LoginModule is shown in Listing 9.9. Listing 9.9. Running Main with Demo2LoginModulejava -Djava.security.auth.login.config==jaasexample/ jaasexample.config jaasexample.Main Demo2 Name: demo Password: demo Login succeeded? true Principals none. Public credentials: none. Private credentials: none. 9.2.2.3 Third ScenarioThe next step after authenticating a principal is to create the associated Principal and credential objects and add them to the Subject passed to the LoginModule . Demo3LoginModule demonstrates this by extending Demo2LoginModule , as shown in Listing 9.10. Listing 9.10. Demo3LoginModule.javapackage jaasexample; import java.util.Set; import java.security.Principal; import javax.security.auth.login.LoginException; /** * If a login() succeeds, the commit() method populates the * Subject by adding Principals and credential objects to the * authenticated Subject; the logout() method will remove * them from the Subject. The abort() method will reset * <code>userName</code>. * This class extends Demo2LoginModule, which performs the * authentication via the login() method. */ public class Demo3LoginModule extends Demo2LoginModule { // Create two hard-coded Principals to use when // populating the subject private Principal principal1 = new DemoPrincipal("Manager"); private Principal principal2 = new DemoPrincipal("Agent"); // Create two hard-coded credentials to use when // populating the subject private String publicCredential = "Louise"; private String privateCredential = "Private DB access password"; /** * When the first phase of the LoginContext.login() * process succeeds, each of the LoginModules need to * associate Principal and credential objects with the * Subject. * This commit() method adds Principals * <code>principal1</code> and <code>principal2</code>, * public credential <code>publicCredential</code> to the * public credentials, and <code>privateCredential</code> * to the privateCredentials for the Subject. * * @return the current boolean value of the * <code>succeeded</code> variable. */ public boolean commit() throws LoginException { if (debug) System.out.println("Demo3LoginModule.commit: " + succeeded); // Add 2 Principals and 2 credentials to the Subject Set principalSet = subject.getPrincipals(); principalSet.add(principal1); principalSet.add(principal2); subject.getPublicCredentials().add(publicCredential); subject.getPrivateCredentials(). add(privateCredential); return succeeded; } /** * During logout processing, remove the state (Principals * and credentials) added by the commit() method to the * Subject, and remove <code>userName</code>. * * @return the boolean <code>true</code>. */ public boolean logout() throws LoginException { if (debug) System.out.println("Demo3LoginModule.logout"); // Remove the Principals and credentials added by // commit() and clear userName Set principalSet = subject.getPrincipals(); principalSet.remove(principal1); principalSet.remove(principal2); subject.getPublicCredentials(). remove(publicCredential); subject.getPrivateCredentials(). remove(privateCredential); userName = null; return true; } /** * Reset the state set up by the login() method since the * login process failed. In this implementation, * <code>userName</code> is reset, and the value of the * <code>succeeded</code> variable is returned. * * @return the current boolean value of the * <code>succeeded</code> variable. */ public boolean abort() throws LoginException { if (debug) System.out.println("Demo3LoginModule.abort"); userName = null; return succeeded; } } The notable differences with the Demo2LoginModule code are that commit() adds Principal and credential objects to the Subject being logged in. The abort() method removes the reference to the user name. The logout() method removes from the Subject the Principal s and credentials that were added to the Subject by commit() . As we said in Section 9.1 on page 289, a credential can be an object of any type, whereas the principals must implement the Principal and Serializable interfaces. The Principal s must implement the Serializable interface because Subject is Serializable . In Demo3LoginModule , the public credential is the String object "Louise" , and the private credential is the String object "Private DB access password" . Demo3LoginModule associates two Principal s with the Subject : principal1 , with name "Manager" ; and principal2 , with name "Agent" . Both of these Principal s are instance of the DemoPrincipal class, whose code is shown in Listing 9.11. Listing 9.11. DemoPrincipal.javapackage jaasexample; import java.io.Serializable; import java.security.Principal; /** * The Principal class used by Demo3LoginModule. * This class must implement Principal and Serializable. */ public class DemoPrincipal implements Principal, Serializable { private String name; // need this for method getName() /** * Create the Principal instance, remembering the name of * the principal. * * @param principalName is a String representing the * principal's name. */ public DemoPrincipal(String principalName) { // Remember this for the getName() method name = principalName; } /** * Return the principal's name. Required by the * Principal interface. * * @return the String representing the name of the * principal. */ public String getName() { return name; } /** * Return the principal's name in a formatted String. * * @return the principal's name in a formatted String. */ public String toString() { return "DemoPrincipal: " + name; } /** * Return the hash code for this object. * * @return the int value representing the hash code for * this object. */ public int hashCode() { return name.hashCode(); } /** * Return the boolean <code>true</code> when the parameter * is the same object as this object; otherwise return * the boolean <code>false</code>. * * @return the boolean <code>true</code> when the * parameter is the same object as this object. * Otherwise return the boolean * <code>false</code>. */ public boolean equals(Object o) { return this == o; } } Surprisingly, the credentials (both public and private) do not need to be Serializable , even though they are part of the Subject . For security reasons, the credentials are not serialized with a Subject . If they were, an attacker could serialize a Subject and then extract the public and private credentials. If the credentials were to include a password or cryptographic key, security would be compromised. Specifically, in the J2SE V1.4 implementation of Subject , the fields that hold references to the credentials are marked as transient so that they will not be serialized. Note that the methods hashCode() and equals() are overridden because the Principal objects are stored in a Set within the Subject . The Demo3 LoginModule configuration file stanza is shown in Listing 9.12. Listing 9.12. Demo3 LoginModule Configuration File StanzaDemo3 { jaasexample.Demo3LoginModule required debug=true; }; In Main.run() , the LoginContext is used to retrieve information about the Subject , along with its Principal s and credentials. The output from running Main with Demo3LoginModule is shown in Listing 9.13. Listing 9.13. Running Main with Demo3LoginModulejava -Djava.security.auth.login.config==jaasexample/jaasexample.config jaasexample.Main Demo3 Demo1LoginModule.initialize Demo1LoginModule.login: true Name: demo Password: demo Demo3LoginModule.commit: true Login succeeded? true Principals Principal: Manager Class: jaasexample.DemoPrincipal Principal: Agent Class: jaasexample.DemoPrincipal Public credentials: Value: Louise Class: java.lang.String Private credentials: Value: Private DB access password Class: java.lang.String Demo3LoginModule.logout The Subject 's Principal objects can be used for authorization purposes. This will be demonstrated in Section 9.3. |
< Day Day Up > |