ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Java and Security, Part 2
Pages: 1, 2, 3, 4, 5, 6, 7, 8

Login Module

We now need to supply a login module, as shown in Example 17-7. This follows the standard JAAS API.

Example 17-7. A login module

package com.oreilly.wlguide.security.provider;

import java.io.IOException;
import java.util.*;
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.spi.LoginModule;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;

/**
 * This login module will be called by our Authentication Provider.
 * It assumes that the option, usermap, will be passed which contains
 * the map of users to passwords and groups.
 */
public class MyLoginModuleImpl implements LoginModule {
   private Subject subject;
   private CallbackHandler callbackHandler;
   private HashMap userMap;
   // Authentication status
   private boolean loginSucceeded;
   private boolean principalsInSubject;
   private Vector principalsBeforeCommit = new Vector( );
   
   public void initialize(Subject subject, CallbackHandler callbackHandler,
   Map sharedState, Map options) {
      this.subject = subject;
      this.callbackHandler = callbackHandler;
      // Fetch user/password map that should be set by the authenticator
      userMap = (HashMap) options.get("usermap");
   }
   
   /* Called once after initialize to try and log the person in */
   public boolean login( ) throws LoginException {
      // First thing we do is create an array of callbacks so that
      // we can get the data from the user
      Callback[] callbacks;
      callbacks = new Callback[2];
      callbacks[0] = new NameCallback("username: ");
      callbacks[1] = new PasswordCallback("password: ", false);
      try {
         callbackHandler.handle(callbacks);
      } catch (IOException eio) {
         throw new LoginException(eio.toString( ));
      } catch (UnsupportedCallbackException eu) {
         throw new LoginException(eu.toString( ));
      }
   
      String username = ((NameCallback) callbacks[0]).getName( );
      char [] pw = ((PasswordCallback) callbacks[1]).getPassword( );
      String password = new String(pw);
      if (username.length( ) > 0) {
         if (!userMap.containsKey(username))
            throw new FailedLoginException("Authentication Failed: Could not find user:
" + username);
         String realPassword = ((MyAuthenticationProviderImpl.MyUserDetails) userMap.
get(username)).getPassword( );
         if (realPassword == null || !realPassword.equals(password))
            throw new FailedLoginException("Authentication Failed: Password incorrect
for user" + username);
      } else {
         // No Username, so anonymous access is being attempted
      }
      loginSucceeded = true;
      // We collect some principals that we would like to add to the user
      // once this is committed.
      // First, we add his username itself
      principalsBeforeCommit.add(new WLSUserImpl(username));
      // Now we add his group
      principalsBeforeCommit.add(new WLSGroupImpl(((MyAuthenticationProviderImpl.
MyUserDetails)userMap.get(username)).getGroup( )));
      return loginSucceeded;
   }

   public boolean commit( ) throws LoginException {
      if (loginSucceeded) {
         subject.getPrincipals().removeAll(principalsBeforeCommit);
         principalsInSubject = true;
         return true;
      } else {
         return false;
      }
   }

   public boolean abort( ) throws LoginException {
      if (principalsInSubject) {
         subject.getPrincipals( ).removeAll(principalsBeforeCommit);
         principalsInSubject = false;
      }
      return true;
   }

   public boolean logout( ) throws LoginException {
      return true;
   }
}

Although long, this is pretty straightforward. Let's go through it. The initialize( ) method has direct access to the options that were configured in the provider. As we put our database in the options, this method provides the ideal place to extract the database. The rest of the action occurs in a combination of the login( ), commit( ), and abort( ) methods. If a login succeeds, and the control flags of the login modules are such that the entire login is to commit, the commit( ) method will be called— otherwise, the abort( ) method will be called. These methods simply ensure that the principals that should be associated with the subject are placed into the subject. The login( ) method does all of the work. First, it sets up a number of callbacks—we need the username and password. Note that the actual callback implementation is going to be handled by WebLogic. For example, WebLogic prompts you for the system user credentials when you try and boot a WebLogic server or access the Administration Console. It is the data from these callbacks that eventually will be supplied to the login method. After handling the callbacks, we extract the username and password of the user and locate it in our database. If successful, we make a list of principals that we want to associate with the user, storing these in the variable principalsBeforeCommit. The principals are added to the subject only if WebLogic calls the commit( ) method.

Deploying the Provider

Once you have created the authentication provider and login module, you can package these together with the generated stub and MBI files. To do this, execute the following command:

java weblogic.management.commo.WebLogicMBeanMaker -MJF myAuth.jar -files .

You are now ready to deploy your new provider. Copy myAuth.jar to the WL_HOME/server/lib/mbeantypes directory, and then reboot the server. Note that all custom providers have to be located in this directory. Start up the Administration Console and navigate to the Security/myrealm/Providers/Authentication node. In the list of available authenticators and identity asserters, you should find an option for "Configure a new My Authenticator." Selecting this option and clicking Create will configure the authenticator. On the following tab, you will notice that you can change the control flag. If you change this to something such as requisite, make sure that your database has a user in the Administrators group. If not, you won't even be able to boot the server! Use the OPTIONAL flag during development to avoid these problems.

Creating an Identity Assertion Provider

Imagine that you have some external system—say, a Java client or perhaps even an external web server—that authenticates a user, and you now want this user to participate in actions involving WebLogic. Furthermore, you don't want WebLogic to reauthenticate the user. Rather, you want to use some token generated by the external system to be used as an automatic WebLogic login. This is fairly typical of many single sign-on scenarios. The key to implementing this is to use an Identity Assertion Provider. Let's look at how you can implement such a scenario.

We are going to take as an example an external Java client that has presumably performed some user authentication, and who now needs to transfer this identity to WebLogic in order to access a protected web application. First of all, let's configure the web application to use identity assertion. Do this by setting the login-config to use a CLIENT-CERT authorization method. As this is standard J2EE, you will need to create a web.xml file with something such as the following in it:

<security-constraint>
<!-- web resource collection omitted -->
<auth-constraint>
<description>nyse</description>
<role-name>mysecrole</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>myrealm</realm-name>
</login-config>
<security-role>
<role-name>mysecrole</role-name>
</security-role>>

Now let's imagine we have a client (written in whatever language you wish) that has already performed some user authentication and now needs to access one of the protected web pages—say, http://10.0.10.10:8001/index.jsp. The following client is such an example:

URL url = new URL("http://10.0.10.10:8001/index.jsp");
URLConnection connection = url.openConnection( );
BufferedReader in = new BufferedReader(new InputStreamReader(
                                       connection.getInputStream( )));
// Read the input stream
in.close( );

Pages: 1, 2, 3, 4, 5, 6, 7, 8

Next Pagearrow