Thursday, February 7, 2008

Accessing secured EJBs on Websphere 6.1 from a standalone ("thin") client

Trying to access a secured EJB on Websphere can give you a hard time, especially if you want do so in a standalone Java client. It's not just a simple matter of creating an InitalContext with appropriate security credentials and hoping that the server will propagate the security context to the EJB container.

Basically, I wanted to access the HumanTaskManager deployed on a Websphere Process Server 6.1 instance from a standalone Java client in order to simulate task completion without human intervention.

The HumanTaskManager is implemented as a 2.1 Enterprise Java Bean. To access and use it you have to follow the standard procedure outlined below:
  1. Create a JNDI InitialContext.
  2. Look up the EJB Home.
  3. Invoke one of the create() methods on the EJB home to create an instance of the target EJB.
  4. Invoke the desired business methods on the previously created EJB instance.
Accessing a secured EJB on Websphere is no exception in that respect. In my case, the client application only threw an exception when I wanted to call a business method on the previously created EJB instance (step 4):

java.rmi.AccessException: CORBA NO_PERMISSION 0x0 No; nested exception is:
org.omg.CORBA.NO_PERMISSION:
>> SERVER (id=4773e3aa, host=XXX.highq-it.de) TRACE START:
>> org.omg.CORBA.NO_PERMISSION: java.rmi.AccessException: ; nested exception is:
com.ibm.websphere.csi.CSIAccessException: SECJ0053E: Authentifizierung für /UNAUTHENTICATED beim Aufrufen von (Bean)com/ibm/task/api/HumanTaskManagerHome query(java.lang.String,java.lang.String,java.lang.String,java.lang.Integer,java.util.TimeZone):1 securityName: /UNAUTHENTICATED;accessID: UNAUTHENTICATED is not granted any of the required roles: TaskAPIUser fehlgeschlagen


An odyssee through the depths of the world wide web began before I had finally gathered the information to get the client to work. To make a long story short: You have to write a programmatic login using JAAS and set the authenticated subject as default subject on the current thread. Furthermore, you have to ensure that your program has the correct Java Runtime, i.e. the correct libraries available and is configured appropriately using JVM arguments.

My method that performs the JAAS login looks as follows:

public void login(String userName, String password) throws LoginException, WSSecurityException, NamingException {
InitialContext ctx = getInitialContext(); // standard procedure
ctx.lookup("");

lc = new LoginContext("WSLogin",
new WSCallbackHandlerImpl(userName, password));


lc.login();
subject = lc.getSubject();

WSSubject.setRunAsSubject(subject);
}

The InitialContext can be created in the standard way, using the correct InitialContextFactory (normally com.ibm.websphere.naming.WsnInitialContextFactory) and provider url (in my case corbaloc:iiop:localhost:2809).
The default lookup ctx.lookup("") is performed so that the target security realm and bootstrap host/port is determined automatically for SecurityServer lookup (see example in http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/topic/com.ibm.websphere.base.doc/info/aes/ae/tsec_pacs.html).

The line WSSubject.setRunAsSubject(subject); sets the previously authenticated subject as the J2EE "run as" identitity on the current thread meaning that all subsequent J2EE calls in the same thread will use this subject for authorization checks.

To get the login process to work, I also included the libraries of the target server runtime in the classpath of the Java program. For more information on how to include only client-specific libraries, please use the above URL as a starting point. Since I am using the Websphere Integration Developer, the easiest way to ensure the correct runtime environment was to add the System JRE of the Websphere Process Server on which the target EJB is running.

Furthermore, the following JVM args had to be added to the program:
-Djava.security.auth.login.config=\pf\wps\properties\wsjaas_client.conf -Dcom.ibm.CORBA.ConfigURL=file:\pf\wps\properties\sas.client.props

This is to pass the information of the used login and SSL/CSI configuration to the program. See http://publib-b.boulder.ibm.com/Redbooks.nsf/RedbookAbstracts/tips0221.html?Open and http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/topic/com.ibm.websphere.base.doc/info/aes/ae/tsec_j2clogin.html for more information on this.

At this point, I encountered another exception which made me getting a bit frustrated in the end:
Forwarded IOR failed with: CAUGHT_EXCEPTION_WHILE_CONFIGURING_SSL_CLIENT_SOCKET Exception=com.ibm.websphere.ssl.SSLException: Missing SSL client config properties.

Finally, a note on http://www-1.ibm.com/support/docview.wss?uid=swg21238509 made me feel easy. In Websphere 6.1, it is also necessary to pass the location of a file called ssl.client.props to the JVM:
-Dcom.ibm.SSL.ConfigURL=file:pf\wps\properties\ssl.client.props

Hope this helps...
Matthias

PS: Yet another helpful piece of information: In the IBM techdocs, a standlalone Java client is refererred to as "thin client".

No comments: