User: stark
Date: 01/01/21 11:59:15
Added: documentation HowTo.Security.html
Log:
This document describes the JBoss server's security architecture in some detail. It
should be sufficient to allow you to configure a simple security setup for testing.
It should also give you a good start to being able to inegrate your own custom
security implementation into JBoss.
Revision Changes Path
1.1 newsite/documentation/HowTo.Security.html
Index: HowTo.Security.html
===================================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>How To Setup JAAS Based Security in JBoss</title>
</head>
<body>
<h1>How To Setup JAAS Based Security in JBoss</h1>
<p>By <a href="mailto:[EMAIL PROTECTED]">Scott Stark</a>
for JBoss-PRE2.1, Jan 20 2001</p>
<h2>Introduction</h2>
This document describes the JBoss server's security architecture in some detail. It
should be sufficient to allow you to configure a simple security setup for testing.
It should also give you a good start to being able to inegrate your own custom
security implementation into JBoss.
<ul>
<li><a href="#Security Model Overview">Security Model Overview</a></li>
<li><a href="#SecurityInterceptor">How to Associate Security With the
Container SecurityInterceptor</a></li>
<li><a href="#Using JaasSecurityManager">Using JaasSecurityManager</a></li>
<li><a href="#The Stateless Session Bean">The Stateless Session Bean</a></li>
<li><a href="#Deploying a Bean with Security">Deploying a Bean with
Security</a></li>
<li><a href="#Key Sequence Diagrams">Key Sequence Diagrams</a></li>
</ul>
<h2><a name="Security Model Overview">Security Model Overview</a></h2>
<p>
The security model in JBoss is based on the server container architecture's pluggable
method interceptors and the fact that the container factory always inserts security
interceptor(org.jboss.ejb.plugins.SecurityInterceptor). For a view of
see <a href="../pictures/Entity_container_call.gif">Entity Container Diagram</a>
for additional details.
</p>
<p>
Integration of custom security requires implementing the two interfaces that the
SecurityInterceptor class uses to externalize its security checks. They are:
<code><pre>
package org.jboss.security;
public interface EJBSecurityManager
{
public boolean isValid(java.security.Principal principal, Object credential);
}
</pre></code>
and:
<code><pre>
package org.jboss.security;
public interface RealmMapping
{
public java.security.Principal getPrincipal(java.security.Principal principal);
public boolean doesUserHaveRole(java.security.Principal principal, Set
roleNames);
}
</pre></code>
JBoss includes a number of sample implementations of both interfaces. These can be
found
in the org.jboss.security.plugins.samples package. There is also a JMX service bean
that can
be used to setup a JAAS based implementation of both interfaces. The JMX bean is
org.jboss.security.JaasSecurityManagerService and the security manager
implementation is
org.jboss.security.JaasSecurityManager. This document will focus on setting up the
JaasSecurityManager via the JaasSecurityManagerService for a trivial stateless
session
bean. Once you can perform the steps documented to secure the example bean, you
should
be able to introduce your own production ready security using this example as a
template.
<h2><a name="#SecurityInterceptor">How to Associate Security With the Container
SecurityInterceptor</a></h2>
Ok, so you know that every EJB container in JBoss includes a SecurityInterceptor
that delegates its security checks to an EJBSecurityManger and RealmMapping
implementation.
Question: How do you choose which implementations a given container uses?
Answer: You specify this information via the jboss deployment descriptor.
<p>
<h3>The JBoss Deployment Descriptor(jboss.xml and standardjboss.xml)</h3>
The JBoss deployment descriptor is the JBoss application specific deployment
configuration file. It describes optional behavior that is outside of the
EJB spec ejb-jar.xml deployment descriptor. The standardjboss.xml version
of the file is located in ${jboss_home}/conf/conf_name where ${jboss_home}
is the directory into which you have installed the JBoss distribution and conf_name
is the specific runtime configuration that you specify to the run.sh or run.bat
script when starting the server. The default value for conf_name is of
course "default". The standardjboss.xml specifies the global configuration
default values. You can also specific ejb-jar or j2ee-ear specific jboss.xml
descriptors that override specific configuration properties as appropriate
for your application.
There are a quite a few configurable properties that can be set in the file,
but all are optional. For all of the possible configuration elements and
their details see the <a href="jboss.dtd">jboss.dtd</a>.
We are only concerned with the two security specific elements:
<ul>
<li>role-mapping-manager</li>
<li>authentication-module</li>
</ul>
</p>
<h4>role-mapping-manager</h4>
The role-mapping-manager element specifies the implementation of the
org.jboss.security.RealmMapping interface that is to be used by the container
SecurityInterceptor. The value is specified as the JNDI name to where the
object is located. Hence, the role-mapping-manager is like a JMS
TopicConnectionFactory
in that it is accessed via a JNDI name. As far as the container configuration is
concerned, an implementation of org.jboss.security.RealmMapping exists in the
JBoss server JNDI namespace and role-mapping-manager element provides the
location. We'll se how you get a RealmMapping instance into the JNDI namespace
shortly.
<h4>authentication-module</h4>
The authentication-module element specifies the implementation of the
org.jboss.security.EJBSecurityManager interface that is to be used by the container
SecurityInterceptor. The value is specified as the JNDI name to where the
object is located, just like the role-mapping-manager.
<h4>Sample jboss.xml</h4>
The jboss.xml descriptor will we use is:
<code><pre>
<?xml version="1.0"?>
<jboss>
<container-configurations>
<container-configuration>
<container-name>Standard Stateless
SessionBean</container-name>
<role-mapping-manager>java:/jaas/other</role-mapping-manager>
<authentication-module>java:/jaas/other</authentication-module>
</container-configuration>
</container-configurations>
<enterprise-beans>
<session>
<ejb-name>StatelessSession</ejb-name>
<configuration-name>Standard Stateless
SessionBean</configuration-name>
</session>
</enterprise-beans>
</jboss>
</pre></code>
This says that we are augmenting the definition of the "Standard Stateless
SessionBean"
container to include role-mapping-manager and authentication-module security
elements,
the values of which are the JNDI name "java:/jaas/other". We will see the reason for
choosing this particular name over the next couple of sections. The "Standard
Stateless SessionBean"
name is coming from the standardjboss.xml default configuration file.
<h3>Setting Up the RealmMapping and EJBSecurityManager in JNDI</h3>
So the container configuration security elements specify the JNDI names where
the desired RealmMapping and EJBSecurityManager implementations are to be
obtained from for a given container. Now the question is how to bind implementations
into the JBoss server JNDI namespace. The answer is to create a JMX mbean that
creates and binds the desired implementations at server startup. The
JaasSecurityManagerService
is an mbean that has been written that we will use to perform the required setup.
<p>
To configure the JaasSecurityManagerService, open the
${jboss_home}/conf/default/jboss.jcml
file and look for an entry like:
<code><pre>
<!-- JAAS security manager and realm mapping -->
<mbean code="org.jboss.security.plugins.JaasSecurityManagerService"
name="DefaultDomain:service=JaasSecurityManager" />
</code></pre>
If it is commented out or does not exist, uncomment or add the entry. The service
creates a reference to a JNDI Context at java:/jaas that lazily binds instances
of JaasSecurityManager under java:/jaas as requested. If you don't know JNDI
well or this just makes no sense, don't worry about. All we care about is that with
the JaasSecurityManagerService setup, any lookup on the JBoss server JNDI
InitialContext
using a name of the form java:/jaas/xyz results in an object of type
org.jboss.security.plugins.JaasSecurityManager that has the name xyz.
Translated to code, this means:
<code><pre>
InitialContext ctx = new InitialContext();
JaasSecurityManager jsm1 = (JaasSecurityManager) ctx.lookup("java:/jaas/xyz");
</code></pre>
where jsm1 is an instance of JaasSecurityManager that was created using the
name "xyz". We are going to use this feature to bind a single instance of
JaasSecurityManager
for use as both the RealmMapping and EJBSecurityManager implementations(because
JaasSecurityManager implements both interfaces). We'll see this when we get to the
session bean example. Now we need to know how we can actually authenticate users
and specify the roles/identies they posses with a JaasSecurityManager.
<h2><a name="Using JaasSecurityManager">Using JaasSecurityManager</a></h2>
As you would expect, the JaasSecurityManager uses the JAAS
(<a href="http://www.javasoft.com/products/jaas">Java Authentication and
Authorization Service</a>) to implement both the user authentication and
role mapping function of the RealmMapping and EJBSecurityManager interfaces.
It does this by creating a JAAS Subject using the
javax.security.auth.login.LoginContext
mechanism. The JAAS Subject creation involves:
<code><pre>
Principal principal = ... passed in by SecurityInterceptor;
char[] password = ... passed in by SecurityInterceptor;
String name = ... the xyz component of java:/jaas/xyz used in the
authentication-module and role-mapping-manager
LoginContext lc = new LoginContext(name, new CallbackHandler(){...});
lc.login(); // This validates principal, password
Subject subject = lc.getSubject();
Set roles = subject.getPrincipals();
</code></pre>
If you know JAAS, you'll see that the name that was used in the creation of the
JaasSecurityManager correlates with the LoginContext Configuration index. The
JAAS LoginContext object looks to a configuration file that is made up of
named sections that describe the LoginModules that need to be executed in
order to perform authentication. This abstraction allows the authentication
api to be independent of a particular implementation. The authentication
of users and the assignment of user roles comes down to implementing a
javax.security.auth.spi.LoginModule and creating login configuration entry
that correlates with the JaasSecurityManager name. There exist a number
of sample LoginModule implementation in the org.jboss.security.plugins.samples
package. We are going to use the JaasServerLoginModule to demonstrate the
how to configure a LoginModule to work with the JaasSecurityManager. If
you need different authentication and role mapping you can choose another
LoginModule or implement you own and then configure it using the same
steps we will use.
<h3><a name="Using JaasServerLoginModule">Using JaasServerLoginModule</a></h3>
The JaasServerLoginModule class is a simple file based implemention that
uses two files(users.properties and roles.properities) to perform authentication
and role mapping respectively.
<h4>users.properties</h4>
The users.properties file is a java properties formatted file that specifies
the username to password mapping. Its format is
<code><pre>
username1=password1
username2=password2
...
</pre></code>
with one entry per line.
<h4>roles.properties</h4>
The roles.properties file is a java properties formatted file that specifies
the username to role(s) mapping. Its format is
<code><pre>
username1=role1[,role2,...]
username2=role1
...
</pre></code>
with one entry per line. If a user has multiple roles they are specified using
a comma separated list.
<h4>The LoginModule Configuration File</h4>
The JAAS LoginModule Configuration file is ${jboss_home)/conf/default/auth.conf.
The syntax is:
<code><pre>
name {
login_module_class_name (required|optional|...) [options];
};
</pre></code>
See the JAAS documentation for the complete syntax description. There should be
an entry like the following:
// The default server login module
<code><pre>
other {
// A realistic server login module, which can be used when the number
// of users is relatively small. It uses two properties files:
// users.properties, which holds users (key) and their password (value).
// roles.properties, which holds users (key) and a comma-separated list of
their roles (value).
org.jboss.security.plugins.samples.JaasServerLoginModule required;
};
</pre></code>
This indicates that the JaasServerLoginModule we want to use is setup for the
"other" configuration. This happens to the the configuration that JAAS uses when
it can't find a match and it will work fine for us.
<p>
We have touched on all of the JBoss security related elements we need to configure.
Let's now put together a simple session bean that we will secure to demonstrate
how to use what we have gone over to deploy a secure bean.
<h2><a name="The Stateless Session Bean">The Stateless Session Bean</a></h2>
Here are the home, remote and bean classes for the simple stateless
session bean we are going to secure, along with a simple client that
creates an instance of the session bean:
<h3>StatelessSession.java</h3>
<code><pre>
import javax.ejb.*;
import java.rmi.*;
public interface StatelessSession extends EJBObject
{
public String echo(String arg) throws RemoteException;
}
</pre></code>
<h3>StatelessSessionHome.java</h3>
<code><pre>
import javax.ejb.*;
import java.rmi.*;
public interface StatelessSessionHome extends EJBHome
{
public StatelessSession create() throws RemoteException, CreateException;
}
</pre></code>
<h3>StatelessSessionBean.java</h3>
<code><pre>
import java.rmi.RemoteException;
import java.security.Principal;
import javax.ejb.*;
public class StatelessSessionBean implements SessionBean
{
private SessionContext sessionContext;
public void ejbCreate() throws RemoteException, CreateException
{
System.out.println("StatelessSessionBean.ejbCreate() called");
}
public void ejbActivate() throws RemoteException
{
System.out.println("StatelessSessionBean.ejbActivate() called");
}
public void ejbPassivate() throws RemoteException
{
System.out.println("StatelessSessionBean.ejbPassivate() called");
}
public void ejbRemove() throws RemoteException
{
System.out.println("StatelessSessionBean.ejbRemove() called");
}
public void setSessionContext(SessionContext context) throws RemoteException
{
sessionContext = context;
}
public String echo(String arg)
{
System.out.println("StatelessSessionBean.echo, arg="+arg);
Principal p = sessionContext.getCallerPrincipal();
System.out.println("StatelessSessionBean.echo, callerPrincipal="+p);
return arg;
}
}
</pre></code>
<h3>ejb-jar.xml</h3>
<code><pre>
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans
1.1//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<display-name>SecurityTests</display-name>
<enterprise-beans>
<session>
<description>A trival echo bean</description>
<ejb-name>StatelessSession</ejb-name>
<home>StatelessSessionHome</home>
<remote>StatelessSession</remote>
<ejb-class>StatelessSessionBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<role-name>Echo</role-name>
</security-role>
<method-permission>
<role-name>Echo</role-name>
<method>
<ejb-name>StatelessSession</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar>
</pre></code>
<h3>StatelessSessionClient.java</h3>
<code><pre>
import java.io.IOException;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
/** Run with -Djava.security.auth.login.config=${jboss_home}/client/auth.conf
where ${jboss_home} is the location of your JBoss distribution.
@author [EMAIL PROTECTED]
@version $Revision: 1.1 $
*/
public class StatelessSessionClient
{
static class AppCallbackHandler implements CallbackHandler
{
private String username;
private char[] password;
public AppCallbackHandler(String username, char[] password)
{
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws
java.io.IOException, UnsupportedCallbackException
{
for (int i = 0; i < callbacks.length; i++)
{
if (callbacks[i] instanceof NameCallback)
{
NameCallback nc = (NameCallback)callbacks[i];
nc.setName(username);
}
else if (callbacks[i] instanceof PasswordCallback)
{
PasswordCallback pc = (PasswordCallback)callbacks[i];
pc.setPassword(password);
}
else
{
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
}
}
}
}
public static void main(String args[]) throws Exception
{
try
{
if( args.length != 2 )
throw new IllegalArgumentException("Usage: username password");
String name = args[0];
char[] password = args[1].toCharArray();
AppCallbackHandler handler = new AppCallbackHandler(name, password);
LoginContext lc = new LoginContext("TestClient", handler);
System.out.println("Created LoginContext");
lc.login();
}
catch (LoginException le)
{
System.out.println("Login failed");
le.printStackTrace();
}
try
{
InitialContext jndiContext = new InitialContext();
StatelessSessionHome home = (StatelessSessionHome)
jndiContext.lookup("StatelessSession");
System.out.println("Found StatelessSessionHome");
StatelessSession bean = home.create();
System.out.println("Created StatelessSession");
System.out.println("Bean.echo('Hello') -> "+bean.echo("Hello"));
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
</code></pre>
The session bean is trivial. The client is also trivial except for the use of a
JAAS LoginContext and CallbackHandler implementation. This is how a client
establishes
the username and password that is sent to jboss. Now, finally let's put everything
together and deploy the session bean.
<h2><a name="Deploying a Bean with Security">Deploying a Bean with Security</a></h2>
We will perform the following steps to deploy and test the secured session bean:
<ol>
<li>Compile the session bean and client</li>
<li>Create the session bean ejb-jar with the ejb-jar.xml and jboss.xml security
elements
</li>
<li>Edit the users.properties and roles.properties</li>
<li>Deploy the session bean jar, users.properties and roles.properties</li>
<li>Edit the JBoss server jboss.jcml and auth.conf files</li>
<li>Start the JBoss server</li>
<li>Setup client env and test access to the session bean</li>
</ol>
<h3>Compile the session bean and client</h3>
The examples I'll go through are on a windows 2000 box using the
<a href="http://sources.redhat.com/cygwin/">cygwin</a> port of the
GNU tools. So most things will look like unix with the exception
of the ';' path separator used in the java classpath.
<p>
First save all of the files presented in this document. You should have the
following 6 files:
<code><pre>
bash 1066>ls
StatelessSession.java StatelessSessionHome.java
StatelessSessionBean.java ejb-jar.xml
StatelessSessionClient.java jboss.xml
</code></pre>
Next, setup the classpath as follows by substituting the value for
jboss_home appropriate for your system.
<code><pre>
bash 1068>export CLASSPATH="${jboss_home}/client/jaas.jar"
bash 1069>CLASSPATH="${CLASSPATH};${jboss_home}/client/ejb.jar"
bash 1070>CLASSPATH="${CLASSPATH};${jboss_home}/client/jnp-client.jar"
bash 1071>CLASSPATH="${CLASSPATH};${jboss_home}/client/jboss-client.jar"
bash 1072>CLASSPATH="${CLASSPATH};."
bash 1073>echo $CLASSPATH
D:/usr/local/src/cvsroot/jBoss/jboss/dist/client/jaas.jar;D:/usr/local/src/cvsroot/jBoss/jboss/dist/client/ejb.jar;D:/usr/local/src/cvsroot/jBoss/jboss/dist/client/jnp-client.jar;D:/usr/local/src/cvsroot/jBoss/jboss/dist/client/jboss-client.jar;.
</code></pre>
Next, compile all of the source.
<code><pre>
bash 1077>javac -g *.java
bash 1078>ls
StatelessSession.class
StatelessSession.java
StatelessSessionBean.class
StatelessSessionBean.java
StatelessSessionClient$AppCallbackHandler.class
StatelessSessionClient.class
StatelessSessionClient.java
StatelessSessionHome.class
StatelessSessionHome.java
ejb-jar.xml
jboss.xml
</code></pre>
<h3>Create the session bean ejb-jar with the ejb-jar.xml and jboss.xml security
elements</h3>
Next, create the session bean jar as follows:
<code><pre>
bash 1087>jar -cf $jboss_home/deploy/ssbean.jar StatelessSession.class
StatelessSessionBean.class StatelessSessionHome.class META-INF
bash 1087>jar -tf $jboss_home/deploy/ssbean.jar
META-INF/
META-INF/MANIFEST.MF
StatelessSession.class
StatelessSessionBean.class
StatelessSessionHome.class
META-INF/ejb-jar.xml
META-INF/jboss.xml
</code></pre>
<h3>Edit the users.properties and roles.properties</h3>
Create a users.properties and roles.properties with the following data in each
file:
<code><pre>
bash 1090>cat users.properties
scott=echoman
stark=javaman
bash 1091>cat roles.properties
scott=Echo
stark=Java,Coder
bash 1092>
</code></pre>
<h3>Deploy the session bean jar, users.properties and roles.properties</h3>
We already deployed the session bean jar by jaring the files to the
$jboss_home/deploy directory.
To deploy the users.properties and roles.properties simply copy them to to the
$jboss_home/conf/default directory.
<h3>Edit the JBoss server jboss.jcml and auth.conf files</h3>
These files needs to be setup as described earlier. The jboss.jcml file needs to
have the JaasSecurityManagerService mbean element:
<code><pre>
...
<!-- JAAS security manager and realm mapping -->
<mbean code="org.jboss.security.plugins.JaasSecurityManagerService"
name="DefaultDomain:service=JaasSecurityManager" />
</code></pre>
and the auth.conf needs to have the JaasServerLoginModule entry in the other section:
<code><pre>
...
// The default server login module
other {
// A realistic server login module, which can be used when the number
// of users is relatively small. It uses two properties files:
// users.properties, which holds users (key) and their password (value).
// roles.properties, which holds users (key) and a comma-separated list of
their roles (value).
org.jboss.security.plugins.samples.JaasServerLoginModule required;
// For database based authentication comment the line above,
// uncomment the line below and adjust the parameters in quotes
// Database server login module provides security manager only, no role mapping
// org.jboss.security.plugins.DatabaseServerLoginModule required
db="jdbc/DbJndiName" table="UserTable" name="UserNameColumn" password="UserPswColumn";
};
</code></pre>
<h3>Start the JBoss server</h3>
Go to the $jboss_home/bin and start the run.sh or run.bat script as appropriate for
you system. You will see a good deal of ouput on your console. Mine looks like, and
I have emphasized the session bean deployment output.
<code><pre>
811>run.bat
Using configuration "default"
[Info] Java version: 1.3.0_01,Sun Microsystems Inc.
[Info] Java VM: Java HotSpot(TM) Client VM 1.3.0_01,Sun Microsystems Inc.
[Info] System: Windows 2000 5.0,x86
[Shutdown] Shutdown hook added
[Service Control] Registered with server
[Jetty] Setting unpackWars=false
[Jetty] Set successfully
[Jetty] Adding configuration:
URL=file:/usr/local/src/cvsroot/jBoss/jboss/dist/conf/default/jetty.xml
[Jetty] Added successfully
[Service Control] Initializing 18 MBeans
[Webserver] Initializing
[Webserver] Initialized
[Naming] Initializing
[Naming] Initialized
...
[J2EE Deployer Default] Starting
[J2EE Deployer Default] Cleaning up deployment directory
[J2EE Deployer Default] Started
[Auto deploy] Starting
[Auto deploy] Watching D:\usr\local\src\cvsroot\jBoss\jboss\dist\deploy
<em>
[Auto deploy] Auto deploy of
file:/D:/usr/local/src/cvsroot/jBoss/jboss/dist/deploy/ssbean.jar
[J2EE Deployer Default] Deploy J2EE application:
file:/D:/usr/local/src/cvsroot/jBoss/jboss/dist/deploy/ssbean.jar
[J2EE Deployer Default] Create application ssbean.jar
[J2EE Deployer Default] install module ssbean.jar
[J2EE Deployer Default] Starting module ssbean.jar
[Container factory]
Deploying:file:/D:/usr/local/src/cvsroot/jBoss/jboss/dist/tmp/deploy/Default/ssbean.jar/ejb1001.jar
</em>
[Container factory] Deprecated container invoker. Change to
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker
[Container factory] Deprecated container invoker. Change to
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker
[Container factory] Deprecated container invoker. Change to
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker
[Container factory] Deprecated container invoker. Change to
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker
<em>
[Verifier] Verifying
file:/D:/usr/local/src/cvsroot/jBoss/jboss/dist/tmp/deploy/Default/ssbean.jar/ejb1001.jar
[Container factory] Deploying StatelessSession
[Container factory] Deployed application:
file:/D:/usr/local/src/cvsroot/jBoss/jboss/dist/tmp/deploy/Default/ssbean.jar/ejb1001.jar
[J2EE Deployer Default] J2EE application:
file:/D:/usr/local/src/cvsroot/jBoss/jboss/dist/deploy/ssbean.jar is deployed.
</em>
[Auto deploy] Started
[JMX RMI Adaptor] Starting
[JMX RMI Adaptor] Started
[JMX RMI Connector] Starting
[JMX RMI Connector] Started
[Service Control] Started 18 services
[Default] JBoss PRE-2.1 Started
</code></pre>
<h3>Setup client env and test access to the session bean</h3>
At this point the session bean is deployed and it should only be accessible by
users with a role of 'Echo', and we have one user with a username 'scott'
and a password 'echoman' that has this role. We have another user with a
username 'stark' and a password 'javaman' that should not be able to acccess
the session bean because he does not have the required role. Let's test this.
<p>
We need one final bit of information in order for the client to find the JBoss
server JNDI name service. Since we are using a no arg InitialContext in the
client, we need a jndi.properties file in our classpath(or we need to specify
all required properities on the command line). For JBoss, the jndi.properties file
should look like the following for the server running on the localhost with the
default name service port:
<code><pre>
bash 1108>cat jndi.properties
# JNDI initial context properties for jboss app server
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost
java.naming.factory.url.pkgs=org.jboss.naming
</code></pre>
Create this file in the same directory as your StatelessSessionClient.java file since
this directory is on the classpath we setup earlier. Now, run the client as user
scott and specify the location of the JBoss client side JAAS login configuration file
as follows:
<code><pre>
bash 1109>java
-Djava.security.auth.login.config=file://${jboss_home}/client/auth.conf
StatelessSessionClient scott echoman
Created LoginContext
Found StatelessSessionHome
Created StatelessSession
Bean.echo('Hello') -> Hello
--- Server console:
[StatelessSession] StatelessSessionBean.ejbCreate() called
[StatelessSession] StatelessSessionBean.echo, arg=Hello
[StatelessSession] StatelessSessionBean.echo, callerPrincipal=scott
</code></pre>
Ok, so that succeed as desired. Now we need to make sure that unauthorized users are
actually denied access. This time run as user stark:
<code><pre>
bash 1111>java
-Djava.security.auth.login.config=file://${jboss_home}/client/auth.conf
StatelessSessionClient stark javaman
Created LoginContext
Found StatelessSessionHome
java.rmi.ServerException: RemoteException occurred in server thread; nested
exception is:
java.rmi.RemoteException: checkSecurityAssociation; nested exception is:
java.lang.SecurityException: Illegal access exception
java.rmi.RemoteException: checkSecurityAssociation; nested exception is:
java.lang.SecurityException: Illegal access exception
java.lang.SecurityException: Illegal access exception
at
sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
at
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:221)
at $Proxy0.create(Unknown Source)
at StatelessSessionClient.main(StatelessSessionClient.java:74)
--- Server console: No new output
</code></pre>
Alright, seems secure. Let's try user scott with an invalid password:
<code><pre>
bash 1113>java
-Djava.security.auth.login.config=file://${jboss_home}/client/auth.conf
StatelessSessionClient scott badpass
Created LoginContext
Found StatelessSessionHome
java.rmi.ServerException: RemoteException occurred in server thread; nested
exception is:
java.rmi.RemoteException: checkSecurityAssociation; nested exception is:
java.lang.SecurityException: Authentication exception
java.rmi.RemoteException: checkSecurityAssociation; nested exception is:
java.lang.SecurityException: Authentication exception
java.lang.SecurityException: Authentication exception
at
sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
at
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:221)
at $Proxy0.create(Unknown Source)
at StatelessSessionClient.main(StatelessSessionClient.java:74)
--- Server console:
[JAASSecurity] Bad password.
[StatelessSession] javax.security.auth.login.FailedLoginException: Password
Incorrect/Password Required
[StatelessSession] at
org.jboss.security.plugins.AbstractServerLoginModule.login(AbstractServerLoginModule.java:110)
[StatelessSession] at
org.jboss.security.plugins.samples.JaasServerLoginModule.login(JaasServerLoginModule.java:94)
[StatelessSession] at java.lang.reflect.Method.invoke(Native Method)
[StatelessSession] at
javax.security.auth.login.LoginContext.invoke(LoginContext.java:595)
[StatelessSession] at
javax.security.auth.login.LoginContext.access$000(LoginContext.java:125)
[StatelessSession] at
javax.security.auth.login.LoginContext$3.run(LoginContext.java:531)
[StatelessSession] at java.security.AccessController.doPrivileged(Native Method)
[StatelessSession] at
javax.security.auth.login.LoginContext.invokeModule(LoginContext.java:528)
[StatelessSession] at
javax.security.auth.login.LoginContext.login(LoginContext.java:449)
[StatelessSession] at
org.jboss.security.plugins.JaasSecurityManager.authenticate(JaasSecurityManager.java:168)
[StatelessSession] at
org.jboss.security.plugins.JaasSecurityManager.isValid(JaasSecurityManager.java:101)
[StatelessSession] at
org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:101)
[StatelessSession] at
org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:124)
[StatelessSession] at
org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:106)
[StatelessSession] at
org.jboss.ejb.StatelessSessionContainer.invokeHome(StatelessSessionContainer.java:253)
[StatelessSession] at
org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invokeHome(JRMPContainerInvoker.java:347)
[StatelessSession] at java.lang.reflect.Method.invoke(Native Method)
[StatelessSession] at
sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
[StatelessSession] at sun.rmi.transport.Transport$1.run(Transport.java:142)
[StatelessSession] at java.security.AccessController.doPrivileged(Native Method)
[StatelessSession] at
sun.rmi.transport.Transport.serviceCall(Transport.java:139)
[StatelessSession] at
sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:443)
[StatelessSession] at
sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:643)
[StatelessSession] at java.lang.Thread.run(Thread.java:484)
</code></pre>
Mission accomplished.
<hr>
<h2><a name="Key Sequence Diagrams">Key Sequence Diagrams</a></h2>
The section provides sequence diagrams of key steps in the security process.
I put these together while going over the PRE2.1 server code to tie together
how the various objects interacted.
<h4>SeverSecurityContext</h4>
<img src="../pictures/SeverSecurityContext.sq.gif">
<h4>ServerSideAuthentication</h4>
<img src="../pictures/ServerSideAuthentication.sq.gif">
<h4>ClientSideAuthentication</h4>
<img src="../pictures/ClientSideAuthentication.sq.gif">
</body>
</html>