Programming WebLogic Security
The following topics are covered in this section:
The Java Authentication and Authorization Service (JAAS) is a standard extension to the security in the Java Software Development Kit version 1.4.1. JAAS provides the ability to enforce access controls based on user identity. JAAS is provided in WebLogic Server as an alternative to the JNDI authentication mechanism.
WebLogic Server clients use the authentication portion of the standard JAAS only. The JAAS LoginContext provides support for the ordered execution of all configured authentication provider LoginModule instances and is responsible for the management of the completion status of each configured provider.
Note the following considerations when using JAAS authentication for Java clients:
weblogic.security.auth.login.UsernamePasswordLoginModule
) only supports username and password authentication. Thus, for client certificate authentication (also referred to as two-way SSL authentication), you should use JNDI. To use JAAS for client certificate authentication, you must write a custom LoginModule that does certificate authentication. Note: If you are going to write your own LoginModule for use with WebLogic Server clients, you must have it call weblogic.security.auth.Authenticate.authenticate()
to perform the login.
weblogic.security.auth.Authenticate.authenticate()
method to perform the login.weblogic.security.auth.Authenticate.authenticate()
. You only need to call the authenticate()
method if you are using WebLogic Server to perform the logon.Note: WebLogic Server provides full container support for JAAS authentication and supports full use of JAAS authentication and authorization in application code.
weblogic.security.services.Authentication.authenticate()
. When using the Authenticate class, weblogic.security.SimpleCallbackHandler
may be a useful helper class.For more information about JAAS, see the Java Authentication and Authorization Service Developer's Guide on the Web at http://java.sun.com/security/jaas/doc/api.html.
Whether the client is an application, applet, Enterprise JavaBean (EJB), or servlet that requires authentication, WebLogic Server uses the Java Authentication and Authorization Service (JAAS) classes to reliably and securely authenticate to the server. JAAS implements a Java version of the Pluggable Authentication Module (PAM) framework, which permits applications to remain independent from underlying authentication technologies. Therefore, the PAM framework allows the use of new or updated authentication technologies without requiring modifications to your Java application.
WebLogic Server uses JAAS for remote Java client authentication, and internally for authentication. Therefore, only developers of custom Authentication providers and developers of remote Java client applications need to be involved with JAAS directly. Users of Web browser clients or developers of within-container Java client applications (for example, those calling an Enterprise JavaBean (EJB) from a servlet) do not require the direct use or knowledge of JAAS.
Note: Both the Java Authentication and Authorization Service (JAAS) and the Java Naming And Directory Interface (JNDI) can be used by Java clients running on WebLogic Server to login to an instance of WebLogic Server in a secure manner, however, JAAS is preferred.
Note: In order to implement security in a WebLogic client you must install the WebLogic Server software distribution kit on the Java client.
The following topics are covered in this section:
To implement Java clients that use JAAS authentication on WebLogic Server, you use a combination of Java SDK 1.4.1 application programming interfaces (APIs) and WebLogic APIs.
Table 3-1 lists and describes the Java SDK APIs packages used to implement JAAS authentication. The information in Table 3-1 is taken from the Java SDK API documentation and annotated to add WebLogic Server specific information. For more information on the Java SDK APIs, see the Javadocs at http://java.sun.com/j2se/1.4.1/docs/api/index.html and http://java.sun.com/j2ee/1.4/docs/api/index.html.
Table 3-2 lists and describes the WebLogic APIs used to implement JAAS authentication. For more information, see Javadocs for WebLogic Classes.
The |
|
The Once the caller has instantiated a If the To logout the For a sample implementation of this class, see Listing 3-4. |
|
This is an abstract class for representing the configuration of LoginModules under an application. The In WebLogic Server, use a login configuration file instead of this class. For a sample configuration file, see Listing 3-3. By default, WebLogic Server uses the Sun Microsystems, Inc. configuration class, which reads from a configuration file. |
|
While application developers write to the Note: WebLogic Server provides an implementation of the LoginModule ( |
|
Implementations of this interface are passed to a
For a sample implementation of this interface, see Listing 3-2. |
|
An application implements a
Underlying security services make requests for different types of information by passing individual For a sample implementation of this interface, see Listing 3-2. |
An authentication class that is used to authenticate user credentials. The WebLogic implementation of the LoginModule ( |
|
Underlying security services use this class to instantiate and pass a The WebLogic implementation of the Note: Application developers should not use this class to retrieve URL information. Instead, they should use the weblogic.security.URLCallbackHandler. |
|
This class implements the WebLogic Server client For a sample implementation, see Listing 3-6. |
|
The class used by application developers for returning a |
At a minimum, a JAAS authentication client application comprises the following components:
The Java client instantiates a LoginContext
object and invokes the login by calling the object's login()
method. The login()
method calls methods in each LoginModule to perform the login and authentication.
The LoginContext also instantiates a new empty javax.security.auth.Subject
object (which represents the user or service being authenticated), constructs the configured LoginModule, and initializes it with this new Subject
and CallbackHandler
.
The LoginContext subsequently retrieves the authenticated Subject by calling the LoginContext's getSubject
method. The LoginContext uses the weblogic.security.Security
class runAs()
method to associate the Subject
identity with the PrivilegedAction
or PrivilegedExceptionAction
to be executed on behalf of the user identity.
The LoginModule utilizes the CallbackHandler
to obtain the user name and password and checks that the name and password are the ones it expects.
If authentication is successful, the LoginModule populates the Subject with a Principal representing the user. The Principal the LoginModule places in the Subject is an instance of Principal
, which is a class implementing the java.security.Principal
interface.
LoginModule files can be written to perform different types of authentication, including username/password authentication and certificate authentication. A client application can include one LoginModule (the minimum requirement) or several LoginModules.
Note: Use of the JAAS javax.security.auth.Subject.doAs
methods in WebLogic Server applications do not associate the Subject with the client actions. You may use the doAs
methods to implement J2SE security in WebLogic Server applications, but such usage is independent of the need to use the Security.runAs()
method.
The CallbackHandler
implements the javax.security.auth.callback.CallbackHandler
interface. The LoginModule uses the CallbackHandler
to communicate with the user and obtain the requested information, such as the username and password.
This file configures the LoginModule(s) to be used in the application. It specifies the location of the LoginModule(s) and, if there are multiple LoginModules, the order in which they are to be executed. Use of this file enables Java applications to remain independent from the authentication technologies, which are defined and implemented using the LoginModule.
ant
build script (build.xml
)For a complete working JAAS authentication client that implements the components described here, see the JAAS sample application in the SAMPLES_HOME\server\examples\src\examples\security\jaas
directory provided with WebLogic Server.
For more information on the basics of JAAS authentication, see Sun's JAAS Authentication Tutorial available at http://java.sun.com/j2se/1.4/docs/guide/security/jaas/tutorials/GeneralAcnOnly.html.
The WebLogic implementation of the LoginModule
class is provided in the WebLogic Server distribution in the weblogic.jar
file, located in the WL_HOME\server\lib
directory.
Note: WebLogic Server supports all callback types defined by JAAS as well as all callback types that extend the JAAS specification.
The UsernamePasswordLoginModule
that is part of the WebLogic Server product checks for existing system user authentication definitions prior to execution and does nothing if they are already defined.
For more information about implementing JAAS LoginModules, see the Java Authentication and Authorization Service Developer's Guide.
The first time you use the implementation of the LoginModule provided by WebLogic Server
(weblogic.security.auth.login.UsernamePasswordLoginModule
) to logon, the specified user becomes the machine-wide default user for the JVM (Java virtual machine). When you execute the weblogic.security.Security.runAs()
method, it associates the specified Subject
with the current thread's access permissions and then executes the action. If a specified Subject
represents a non-privileged user (users that are not assigned to any groups are considered non-privileged), the JVM-wide default user is used. Therefore, it is important make sure that the runAs()
method specifies the desired Subject
. You can do this using one of the following options:
main()
, implement the wrapper code shown in Listing 3-1 in the client code.Listing 3-1 runAs() Method Wrapper Code
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import weblogic.security.Security;
public class client
{
public static void main(String[] args)
{
Security.runAs(new Subject(),
new PrivilegedAction() {
public Object run() {
//
//If implementing in client code, main() goes here.
//return null;
}
});
}
}
main()
, implement the wrapper code shown in Listing 3-1 on each thread's run()
method.
To use JAAS in a WebLogic Server Java client to authenticate a subject, perform the following procedure:
LoginModule
classes for the authentication mechanisms you want to use with WebLogic Server. You will need a LoginModule class for each type of authentication mechanism. You can have multiple LoginModule classes for a single WebLogic Server deployment. For information on how to implement the LoginModule
class, see the Java Authentication and Authorization Service (JAAS) 1.0 Developer's Guide available at http://java.sun.com/security/jaas/doc/api.htmlNote: BEA recommends that you use the implementation of the LoginModule provided by WebLogic Server
(weblogic.security.auth.login.UsernamePasswordLoginModule
) for username/password authentication. If you so desire, you can write your own LoginModule
for username/password authentication, however, do not attempt to modify the WebLogic Server LoginModule
and reuse it. If you are going to write your own LoginModule, you must have it call the weblogic.security.auth.Authenticate.authenticate()
method to perform the login. If you are using a remote login system such as SAML you do not need to call the authenticate()
method. You only need to call authenticate()
if you are using WebLogic Server to perform the logon.
The weblogic.security.auth.Authenticate
class uses a JNDI Environment object for initial context as described in Table 3-3.
CallbackHandler
class that the LoginModule will use to communicate with the user and obtain the requested information, such as the username, password, and URL. The URL can be the URL of a WebLogic cluster, providing the client with the benefits of server failover. See Listing 3-2 for the sample CallbackHandler
used in the JAAS client sample provided in the WebLogic Server distribution.Note: Instead of implementing your own CallbackHandler
class, you can use either of two WebLogic-supplied CallbackHandler
classes, weblogic.security.SimpleCallbackHandler
or weblogic.security.URLCallbackHandler
. For more information on these classes, see Javadocs for WebLogic Classes.
Listing 3-2 Implementation of the CallbackHandler Interface
package examples.security.jaas;
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 examples.utils.common.ExampleUtils;
/**
* SampleCallbackHandler.java
* Implementation of the CallbackHandler Interface
*
* @author Copyright (c) 2000-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
class SampleCallbackHandler implements CallbackHandler
{
private String username = null;
private String password = null;
private String url = null;
public SampleCallbackHandler() { }
public SampleCallbackHandler(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:
ExampleUtils.log(toc.getMessage());
break;
case TextOutputCallback.ERROR:
ExampleUtils.log("ERROR: " + toc.getMessage());
break;
case TextOutputCallback.WARNING:
ExampleUtils.log("WARNING: " + toc.getMessage());
break;
default:
throw new IOException("Unsupported message type: " +
toc.getMessageType());
}
}
else if(callbacks[i] instanceof NameCallback)
{
// If username not supplied on cmd line, prompt the user
// for the username.
NameCallback nc = (NameCallback)callbacks[i];
if (ExampleUtils.isEmpty(username)) {
System.err.print(nc.getPrompt());
System.err.flush();
nc.setName((new BufferedReader(new
InputStreamReader(System.in))).readLine());
}
else {
ExampleUtils.log("username: "+username);
nc.setName(username);
}
}
else if(callbacks[i] instanceof URLCallback)
{
// If url not supplied on cmd line, prompt the user for the
// url.
// This example requires the url.
URLCallback uc = (URLCallback)callbacks[i];
if (ExampleUtils.isEmpty(url)) {
System.err.print(uc.getPrompt());\
System.err.flush();
uc.setURL((new BufferedReader(new
InputStreamReader(System.in))).readLine());\
}
else {
ExampleUtils.log("URL: "+url);
uc.setURL(url);
}
}
else if(callbacks[i] instanceof PasswordCallback)
{
PasswordCallback pc = (PasswordCallback)callbacks[i];
// If password not supplied on cmd line, prompt the user
// for the password.
if (ExampleUtils.isEmpty(password)) {
System.err.print(pc.getPrompt());
System.err.flush();
// Note: JAAS specifies that the password is a char[]
// rather than a String.
String tmpPassword = (new BufferedReader(new
InputStreamReader(System.in))).readLine();
int passLen = tmpPassword.length();
char[] passwordArray = new char[passLen];
for(int passIdx = 0; passIdx < passLen; passIdx++)
passwordArray[passIdx] = tmpPassword.charAt(passIdx);
pc.setPassword(passwordArray);
}
else {
String tPass = new String();
for(int p = 0; p < password.length(); p++)
tPass += "*";
ExampleUtils.log("password: "+tPass);
pc.setPassword(password.toCharArray());
}
}
else if(callbacks[i] instanceof TextInputCallback)
{
// Prompt the user for the username
TextInputCallback callback =
(TextInputCallback)callbacks[i];
System.err.print(callback.getPrompt());
System.err.flush();
callback.setText((new BufferedReader(new
InputStreamReader(System.in))).readLine());
}
else
{
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
}
}
}
}
Listing 3-3 sample_jaas.config Code Example
/** Login Configuration for the JAAS Sample Application **/
Sample {
weblogic.security.auth.login.UsernamePasswordLoginModule
required debug=false;
};
LoginContext
. The LoginContext
consults the configuration file, sample_jaas.config
, to load the default LoginModule configured for WebLogic Server. See Listing 3-4 for an example LoginContext
instantiation.Listing 3-4 LoginContext Code Fragment
...
import javax.security.auth.login.LoginContext;
...
LoginContext loginContext = null;
try
{
// Create LoginContext; specify username/password login module
loginContext = new LoginContext("Sample",
new SampleCallbackHandler(username, password, url));
}
Note: If you use another means to authenticate the user such as an Identity Assertion provider or a remote instance of WebLogic Server, the default LoginModule is determined by the remote resource.
login()
method of the LoginContext
instance. The login()
method invokes all the loaded LoginModules. Each LoginModule attempts to authenticate the subject. The LoginContext
throws a LoginException
if the configured login conditions are not met. See Listing 3-5 for an example of the login()
method.Listing 3-5 Login() Method Code Fragment
...
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.CredentialExpiredException;
...
/**
* Attempt authentication
*/
try
{
// If we return without an exception, authentication succeeded
loginContext.login();
}
catch(FailedLoginException fle)
{
System.out.println("Authentication Failed, " +
fle.getMessage());
System.exit(-1);
}
catch(AccountExpiredException aee)
{
System.out.println("Authentication Failed: Account Expired");
System.exit(-1);
}
catch(CredentialExpiredException cee)
{
System.out.println("Authentication Failed: Credentials
Expired");
System.exit(-1);
}
catch(Exception e)
{
System.out.println("Authentication Failed: Unexpected
Exception, " + e.getMessage());
e.printStackTrace();
System.exit(-1);
}
LoginContext
instance using the javax.security.auth.Subject.getSubject() method
and call the action as the Subject. Upon successful authentication of a Subject, access controls can be placed upon that Subject by invoking the weblogic.security.Security
.runAs()
method. The runAs()
method associates the specified Subject with the current thread's access permissions and then executes the action. See Listing 3-6 for an example implementation of the getSubject()
and runAs()
methods.Note: Use of the JAAS javax.security.auth.Subject.doAs
methods in WebLogic Server applications do not associate the Subject with the client actions. You may use the doAs
methods to implement J2SE security in WebLogic Server applications, but such usage is independent of the need to use the Security.runAs()
method.
Listing 3-6 getSubject() and runAs() Methods Code Fragment
...
/**
* Retrieve authenticated subject, perform SampleAction as Subject
*/
Subject subject = loginContext.getSubject();
SampleAction sampleAction = new SampleAction(url);
Security.runAs(subject, sampleAction);
System.exit(0);
...
javax.security.PrivilegedAction
class that executes an EJB to trade stocks.Listing 3-7 Example of a PrivilegedAction Implementation
package examples.security.jaas;
import java.security.PrivilegedAction;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.FinderException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import java.rmi.RemoteException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import examples.ejb20.basic.statelessSession.TraderHome;
import examples.ejb20.basic.statelessSession.Trader;
import examples.utils.common.ExampleUtils;
/**
* SampleAction.java
*
* JAAS sample PrivilegedAction Implementation
*
* @author Copyright (c) 2000-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
public class SampleAction implements PrivilegedAction
{
private static final String JNDI_NAME =
"ejb20-statelessSession-TraderHome";
private String url;
public SampleAction(String url)
{
this.url = url;
}
public Object run()
{
Object obj = null;
try {
callTraderEJB();
}
catch(Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* Call Trader EJB.
*/
public void callTraderEJB()
throws NamingException, CreateException, RemoteException,
RemoveException
{
TraderHome home = lookupTraderHome();
// create a Trader
ExampleUtils.log("Creating a trader");
Trader trader = (Trader)ExampleUtils.narrow(home.create(),
Trader.class);
String [] stocks = {"BEAS", "MSFT", "AMZN", "HWP" };
// execute some buys
for (int i=0; i<stocks.length; i++) {
int shares = (i+1) * 100;
ExampleUtils.log("Buying "+shares+" shares of
"+stocks[i]+".");
trader.buy(stocks[i], shares);
}
// execute some sells
for (int i=0; i<stocks.length; i++) {
int shares = (i+1) * 100;
ExampleUtils.log("Selling "+shares+" shares of
"+stocks[i]+".");
trader.sell(stocks[i], shares);
}
// remove the Trader
ExampleUtils.log("Removing the trader");
trader.remove();
}
/**
* Look up the bean's home interface using JNDI.
*/
private TraderHome lookupTraderHome()
throws NamingException
{
Context ctx = ExampleUtils.getInitialContext(url);
Object home = (TraderHome)ctx.lookup(JNDI_NAME);
return (TraderHome)ExampleUtils.narrow(home, TraderHome.class);
}
}
logout()
method of the LoginContext
instance. The logout()
method closes the user's session and clear the Subject
. See Listing 3-8 for an example of the login()
method.Listing 3-8 logout() Method Code Example
...
import javax.security.auth.login.LoginContext;
...
try
{
System.out.println("logging out...");
loginContext.logout();
}
Note: The LoginModule.logout()
method is never called for a WebLogic Authentication provider or a custom Authentication provider. This is simply because once the Principals
are created and placed into a Subject
, the WebLogic Security Framework no longer controls the lifecycle of the Subject
. Therefore, the developer-written, user code that creates the JAAS LoginContext
to login and obtain the Subject
should also call the LoginContext
to logout. Calling LoginContext.logout()
results in the clearing of the Principals
from the Subject
.
Java clients use the Java Naming and Directory Interface (JNDI) to pass credentials to WebLogic Server. A Java client establishes a connection with WebLogic Server by getting a JNDI InitialContext.
The Java client then uses the InitialContext
to look up the resources it needs in the WebLogic Server JNDI tree.
Note: JAAS is the preferred method of authentication, however, the WebLogic Authentication provider's LoginModule only supports user name and password authentication. Thus, for client certificate authentication (also referred to as two-way SSL authentication), you should use JNDI. To use JAAS for client certificate authentication, you must write a custom Authentication provider whose LoginModule does certificate authentication. For information on how to write LoginModules, see http://java.sun.com/j2se/1.4.1/docs/guide/security/jaas/JAASLMDevGuide.html.
To specify a user and the user's credentials, set the JNDI properties listed in Table 3-3.
Provides an entry point into the WebLogic Server environment. The class weblogic.jndi.WLInitialContextFactory is the JNDI SPI for WebLogic Server. |
|
Specifies the host and port of the WebLogic Server that provides the name service. For example: |
|
Specifies the identity of the user when that user authenticates to the default (active) security realm. |
|
Specifies the credentials of the user when that user authenticates to the default (active) security realm. |
These properties are stored in a hash table that is passed to the InitialContext
constructor. Listing 3-9 illustrates how to use JNDI authentication in a Java client running on WebLogic Server.
Listing 3-9 Example of Authentication
...
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL, "t3://weblogic:7001");
env.put(Context.SECURITY_PRINCIPAL, "javaclient");
env.put(Context.SECURITY_CREDENTIALS, "javaclientpassword");
ctx = new InitialContext(env);
Note: For information on JNDI contexts and threads and how to avoid potential JNDI context problems, see JNDI Contexts and Threads and How to Avoid JNDI Context Problems in the Programming WebLogic JNDI.
A complete working JAAS authentication sample is provided with the WebLogic Server product. The sample is located in the SAMPLES_HOME\server\examples\src\examples\security\jaas
directory. For a description of the sample and instructions on how to build, configure, and run this sample, see the package.html
file in the sample directory. You can modify this code example and reuse it.