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

Creating a Custom Authentication Provider

Developing your own security providers is a relatively specialized task—it is necessary only if WebLogic's default providers are insufficient. The majority of custom providers tend to change the default authentication or identity assertion mechanisms. The following sections provide an example of each of these. We recommend that you read WebLogic's well-documented security provider API to understand the life cycle of each provider if you intend to create your own. This information is supplied on the official web sit. BEA's dev2dev web site also contains a number of example providers.

MBeans

WebLogic's provider architecture is MBean-based (see Chapter 20)—if you are going to write a new provider, it has to have a corresponding MBean implementation. WebLogic provides tools for creating the necessary MBean deployment files and implementations. At runtime, the MBean representing your provider will be used to create an instance of your provider implementation—the MBean is, in a sense, a factory for the provider implementation that you will have to supply. This, in turn, will use the MBean to read its configuration information. Any provider MBean must extend the appropriate base MBean type, supplied with WebLogic. To facilitate in the creation of these peripheral classes, WebLogic provides a few utilities. They are based around an MBean Definition File (MDF), an XML file that describes your MBean implementation. Example 17-5 lists such an XML file.

Example 17-5. A simple MDF (MyAuthentication.xml) for an authentication provider

<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">
<MbeanType Name = "MyAuthenticator" DisplayName = "MyAuthenticator"
  Package = "com.oreilly.wlguide.security.iap"
  Extends = "weblogic.management.security.authentication.Authenticator"
  PersistPolicy = "OnUpdate">
<MbeanAttribute Name = "ProviderClassName" Type = "java.lang.String"
  Writeable = "false"
  Default =
  "&quot;com.oreilly.wlguide.security.iap.MyAuthenticationProviderImpl&quot;"
  />
<MbeanAttribute Name = "Description" Type = "java.lang.String"
  Writeable = "false"
  Default = "&quot;O'Reilly Authentication Provider&quot;"
  />
<MBeanAttribute Name = "Version" Type = "java.lang.String"
  Writeable = "false" Default = "&quot;1.0&quot;"
  />
</MBeanType>

If you are to implement your own authentication provider, copy Example 17-5 verbatim, changing only the lines that have been highlighted (unless you want to add additional attributes). The ProviderClassName MBean attribute is the most important. This indicates the class name that represents the custom provider that we will have to implement, in this case, com.oreilly.wlguide.security.iap.MyAuthentication-ProviderImpl.

Once you have this file, you can use the MbeanMaker utility to take the MDF file and generate the MBean and stubs:

java -DcreateStubs="true" weblogic.management.commo.WebLogicMBeanMaker -MDF ..\
MyAuthentication.xml -files outdirectory

This generates a number of files, all of which can be ignored unless you implement custom operations and attributes. These are all placed in the outdirectory. All you need to do now is supply the actual implementation of the provider, recompile and repackage the whole lot, and deploy it.

Note that to use these utilities, you not only need to execute the setEnv script to prepare your environment, but you also have to add an extra JAR file to your classpath. This JAR file is located in WL_HOME/server/lib/mbeantypes/wlManagement.jar.

Authentication Provider

Now we turn to the authentication provider implementation itself. Recall that in the MDF, we specified the class name of a class that is to supply the authentication provider (see Example 17-6). This class must implement the weblogic.security.spi.AuthenticationProvider interface.

Example 17-6. An authentication provider implementation

package com.oreilly.wlguide.security.iap;

import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.spi.*;

public final class MyAuthenticationProviderImpl implements AuthenticationProvider {
   private String description;
   private LoginModuleControlFlag controlFlag;

   // Our mapping of users to passwords/groups, instead of being in LDAP or in a
   // database, is represented by a HashMap of MyUserDetails objects..
   public class MyUserDetails {
      String pw;
      String group;
      // We use this to represent the user's groups and passwords
      public MyUserDetails(String pw, String group) {
         this.pw=pw; this.group = group;
      }  
      public String getPassword( ) {return pw;}
      public String getGroup( ) {return group;}
   }

   // This is our database
   private HashMap userGroupMapping = null;
   
   public void initialize(ProviderMBean mbean, SecurityServices services) {
      MyAuthenticatorMBean myMBean = (MyAuthenticatorMBean)mbean;
      description = myMBean.getDescription( ) + "\n" + myMBean.getVersion( );
      System.err.println("#In realm:" + myMBean.getRealm( ).wls_getDisplayName( ));
      // We would typically use the realm name to find the database
      // we want to use for authentication. Here, we just create one.
      userGroupMapping = new HashMap( );
      userGroupMapping.put("a", new MyUserDetails("passworda", "g1"));
      userGroupMapping.put("b", new MyUserDetails("passwordb", "g2"));
      userGroupMapping.put("system", new MyUserDetails("12341234",
                           "Administrators"));
      
	  String flag = myMBean.getControlFlag( );
      if (flag.equalsIgnoreCase("REQUIRED")) {
         controlFlag = LoginModuleControlFlag.REQUIRED;
      } else if (flag.equalsIgnoreCase("OPTIONAL")) {
         controlFlag = LoginModuleControlFlag.OPTIONAL;
      } else if (flag.equalsIgnoreCase("REQUISITE")) {
         controlFlag = LoginModuleControlFlag.REQUISITE;
      } else if (flag.equalsIgnoreCase("SUFFICIENT")) {
         controlFlag = LoginModuleControlFlag.SUFFICIENT;
      } else {
         throw new IllegalArgumentException("Invalid control flag " + flag);
      }
   }

   public AppConfigurationEntry getLoginModuleConfiguration( ) {
      HashMap options = new HashMap( );
      options.put("usermap", userGroupMapping);
      return new AppConfigurationEntry(
           "com.oreilly.wlguide.security.provider.MyLoginModuleImpl",
           controlFlag, options
      );
   }
   public String getDescription( ) {
      return description;
   }
   public PrincipalValidator getPrincipalValidator( ) {
      return new PrincipalValidatorImpl( );
   }
   public AppConfigurationEntry getAssertionModuleConfiguration( ) {
      return null;
   }
   public IdentityAsserter getIdentityAsserter( ) {
      return null;
   }
   public void shutdown( ) {}
   }

The class simply provides a way of getting to the various modules, most of which are optional. For example, we do not provide an identity asserter, so we just return null. Likewise, we simply return WebLogic's default principal validator implementation as the principal validator. All of the action happens in the initialize( ) and getLoginModuleConfiguration( ) methods.

Typically, the job of the initialize( ) method is to set up the resources necessary to perform any authentication. As you can see from the implementation, it has direct access to the MBean—so, if you added extra attributes or operations to the MBean, they can be used here. In our case, we simply set up a local database in the form of a hashmap. It then stores the control flag that is used in the module configuration. This eventually will get passed to the login module.

The getLoginModuleConfiguration( ) method returns an object that encapsulates the login module—the final piece of functionality that we have to implement. You can think of this method as playing the same role as the JAAS configuration file. It is instructing the provider as to which login module to use, on the server side this time. Note that the login module is wrapped in an object (AppConfigurationEntry) that also captures the control flag and options.

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

Next Pagearrow