JAAS Based Security in JBoss

Custom Security in JBoss Using the JBossSX Framework

Author:Scott Stark <Scott_Stark@displayscape.com>

Note

This document applies to JBoss release 2.2.2 and later. If your using an earlier version of JBoss upgrade to the 2.2.2 version.

Introduction

This document describes the setting up secured access to JBoss hosted EJBs and web applications for the standard declarative J2EE security model. It should be sufficient to allow you to configure a simple security setup for testing and also give you a good start to being able to integrate your own custom security infrastructure into JBoss. For a more detailed description of the JBossSX framework see the JBossSX chapter.

Security Model Overview

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 the security interceptor (org.jboss.ejb.plugins.SecurityInterceptor). The SecurityInterceptor delegates the tasks of principal authentication and principal role mapping to two different security interfaces: org.jboss.security.EJBSecurityManager and org.jboss.security.RealmMapping. JBoss includes a number of sample implementations of both interfaces which can be found in the org.jboss.security.plugins.samples package.

The default security implementation that comes pre-configured consists of a JMX service bean and a JAAS based implementation of both interfaces. The JMX bean is org.jboss.security.plugins.JaasSecurityManagerService and the security interfaces implementation is org.jboss.security.plugins.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.

How to Associate Security With the Container SecurityInterceptor

Ok, so you know that every EJB container in JBoss includes a SecurityInterceptor that delegates its security checks to a security manager implementation. How do you choose which implementations a given container uses? You specify this information via the jboss deployment descriptor.

The JBoss Deployment Descriptor(jboss.xml and standardjboss.xml)

The JBoss deployment descriptor is the JBoss application specific deployment configuration file. It describes implementation 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 "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 or all 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 jboss.dtd. We are only concerned with the three security specific elements:

  • security-domain

  • role-mapping-manager

  • authentication-module

security-domain

The security-domain element specifies an implementation of both the org.jboss.security.RealmMapping and org.jboss.security.EJBSecurityManager interfaces to use for all J2EE deployment units in the ear or ejb-jar. The value is specified as the JNDI name where the object is located. Hence, the security-domain is like a JMS TopicConnectionFactory in that it is accessed via a JNDI name whose setup is a managed process.

role-mapping-manager

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 where the object is located. 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.

authentication-module

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.

Sample jboss.xml

A sample jboss.xml descriptor is:

Figure 11.8. jboss.xml

<?xml version="1.0"?>
<jboss>
    <!-- All bean containers use this security manager by default -->
    <security-domain>java:/jaas/example1</security-domain> 1
    <container-configurations>
        <!-- Override the role mapping function from that of the
        security-domain setting for stateless session beans -->
        <container-configuration>
            <!-- Use the standardjboss.xml container-name so we only have
            to specify the elements we want to override -->
            <container-name>Standard Stateless SessionBean</container-name>
            <role-mapping-manager>java:/jaas/session-roles</role-mapping-manager>2
        </container-configuration>
    </container-configurations>

</jboss>
1

Establishes a global security manager via thesecurity-domain element.

2

Overrides the global security manager role mapping function for stateless session beans.

Here we are assigning a global security manager for all beans to the the object located at java:/jaas/example1 and we are setting a different role mapping manager for the “Standard Stateless SessionBean” container. This means that any stateless session beans bundled in the ear or jar will use the RealmMapper located at java:/jaas/session-roles rather the the security-domain element setting. We will see the reason for choosing JNDI names of the form java:/jaas/XXX over the next couple of sections.

The JBoss Web Deployment Descriptor(jboss-web.xml)

The jboss-web.xml deployment descriptor is the JBoss application specific deployment used to set the security manager and JNDI bindings for web applications. Like the jboss.xml it uses a security-domain element to declare the JNDI name of the security manager that will perform authentication and authorization of users attempting to access secured content. An example jboss-web.xml descriptor that uses the same security manager used to secure EJBs is given in Figure 11.9.

Figure 11.9. jboss.xml

<?xml version="1.0"?>
<jboss-web>
    <!-- All secured web content uses this security manager -->
    <security-domain>java:/jaas/example1</security-domain>
</jboss-web>

Setting Up the Security Manager Implementation in JNDI

So we have setup the container configuration security elements to specify the JNDI names where the desired RealmMapping and EJBSecurityManager implementations are to be obtained from. 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.

To configure the JaasSecurityManagerService, open the ${jboss_home}/conf/default/jboss.jcml file and look for an entry like:

  <!-- JAAS security manager and realm mapping -->
  <mbean code="org.jboss.security.plugins.JaasSecurityManagerService" name="Security:name=JaasSecurityManager">
    <attribute name="SecurityManagerClassName">org.jboss.security.plugins.JaasSecurityManager</attribute>
    <attribute name="SecurityProxyFactoryClassName">org.jboss.security.SubjectSecurityProxyFactory</attribute>
  </mbean>

If it is commented out or does not exist, uncomment or add the entry. The JaasSecurityManagerService service creates a reference to a JNDI Context at java:/jaas that lazily binds instances of org.jboss.security.plugins.JaasSecurityManager under java:/jaas as they are requested via JNDI. The details of how this happens are not important(if they are to you, look at the code). 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:

InitialContext ctx = new InitialContext();
JaasSecurityManager jsm1 = (JaasSecurityManager) ctx.lookup("java:/jaas/xyz");
String securityDomain = jsm1.getSecurityDomain();
// securityDomain == "xyz"

where jsm1 is an instance of JaasSecurityManager that was created using the name "xyz". We are using this feature to bind a single instance of JaasSecurityManager for use as both the RealmMapping and EJBSecurityManager implementations in the preceeding jboss.xml descriptor. We can do this because JaasSecurityManager implements both interfaces. Now we need to know how we can actually authenticate users and specify the roles/identies they possess with a JaasSecurityManager.

Using JaasSecurityManager

As you would expect, the JaasSecurityManager uses JAAS (Java Authentication and Authorization Service) 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. When the JaasSecurityManager needs to authenticate a user, it does a JAAS login using the following programmatic steps:

Principal principal = ... passed in by SecurityInterceptor;1
Object credential = ... passed in by SecurityInterceptor;2
/* Access the security domain to which the security manager is bound. This is
the xyz component of java:/jaas/xyz name used when defining the security-domain
or role-mapping-manager config elements. */
String name = getSecurityDomain();
CallbackHandler handler = new org.jboss.security.plugins.SecurityAssociationHandler();
handler.setSecurityInfo(principal, credential);
LoginContext lc = new LoginContext(name, handler);
// Validate principal, credential using the LoginModules configured for 'name'
lc.login();
Subject subject = lc.getSubject();
Set subjectGroups = subject.getPrincipals(Group.class);
// Get the Group whose name is 'Roles'
Group roles = getGroup(subjectGroups, "Roles");

	
1

A Principal is an identity object. Often it represents the username string, but it can be an X509 cert, an http cookie, etc. This is ultimately passed to the LoginModule chain and so the interpretation of what the Principal is depends on the configured LoginModules for the security domain.

2

The credential is the value the principal is attempting to use to verify his identity. It could be a password string or one-way has of the password, a session key or token, etc. This is ultimately passed to the LoginModule chain and so the interpretation of what the credential is depends on the configured LoginModules for the security domain.

If you are familiar JAAS, you'll see that the name that was used in the creation of the JaasSecurityManager correlates with the LoginContext Configuration name. The JAAS LoginContext object looks to a configuration object 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 LoginModule implementations in the org.jboss.security.auth.spi package. We are going to go over the use of the simple UsersRolesLoginModule as well as the production read DatabaseServerLoginModule to demonstrate how to configure LoginModules to work with the JaasSecurityManager. You can choose from among the existing LoginModule implementations the one that best integrates with your security environment, or implement you own and then configure it using the same steps we will use.

Using UsersRolesLoginModule

The UsersRolesLoginModule class is a simple properties file based implemention that uses two Java Properties(users.properties and roles.properities) to perform authentication and role mapping respectively.

users.properties

The users.properties file is a java properties formatted file that specifies the username to password mapping. Its format is:

username1=password1
username2=password2
...
with one entry per line.

roles.properties

The roles.properties file is a java properties formatted file that specifies the username to role(s) mapping. Its format is:

username1=role1[,role2,...]
username2=role1
...
with one entry per line. If a user has multiple roles they are specified using a comma separated list. You can also specify groups of roles using a syntax like:
username1.GroupName1=role1[,role2,...]
username2.GroupName2=role1
...
When no GroupName is specified a group name of 'Roles' is implied.

The LoginModule Configuration File

By default JAAS uses a LoginModule configuration file to describe which LoginModule instances need to be executed during a login. The default config for the JBoss server is ${jboss_home)/conf/default/auth.conf. The syntax is:

name {
        login_module_class_name (required|optional|...)
        [options]
        ;
};
See the JAAS documentation for the complete syntax description. An example auth.conf file with two configurations is given below in Figure 11.10.

Figure 11.10. The JBoss Server JAAS Login Config File ($jboss_home/conf/default/auth.conf)

example1 {
// A properties file LoginModule that supports CallerPrincipal mapping
    org.jboss.security.auth.spi.UsersRolesLoginModule required
    ;
};

example2 {
/* A JDBC based LoginModule
LoginModule options:
dsJndiName: The name of the DataSource of the database containing the Principals, Roles tables
principalsQuery: The prepared statement query equivalent to:
    "select Password from Principals where PrincipalID=?"
rolesQuery: The prepared statement query equivalent to:
    "select Role, RoleGroup from Roles where PrincipalID=?"
*/
    org.jboss.security.auth.spi.DatabaseServerLoginModule required
    dsJndiName="java:/DefaultDS"
    principalsQuery="select Password from Principals where PrincipalID=?"
    rolesQuery="select Role, RoleGroup from Roles where PrincipalID=?"
    ;
};

This indicates that the UsersRolesLoginModule we want to use is setup for the configuration named 'example1'. This name also matches name of the security domain portion of the JNDI name java:/jaas/example1 used as the security-domain element in the sample jboss.xml file shown in Figure 11.8. The correlation between the security-domain element value and the login config file entry determines which LoginModules executed by the JaasSecurityManager to perform authentication and authorization. When a user attempts to execute methods on EJBs secured under the java:/jaas/example1 security domain, the user will be authenticated against the UsersRolesLoginModule since this the the LoginModule configured under the example1 name in the server auth.conf file.

There is also a client side version of the auth.conf that is used by the client connecting to JBoss. It is located in ${jboss_home}/client/auth.conf and the default version contents are given in Figure 11.11. The key entry here is the 'other' entry that contains the 'org.jboss.security.ClientLoginModule required;' setting.

Figure 11.11. The JBoss Client JAAS Login Config File ($jboss_home/client/auth.conf)

srp {
    // Example client auth.conf for using the SRPLoginModule
    org.jboss.srp.jaas.SRPLoginModule required
        password-stacking="useFirstPass"
        principalClassName="org.jboss.security.SimplePrincipal"
        srpServerJndiName="SRPServerInterface"
        debug=true
        ;

    // jBoss LoginModule
    org.jboss.security.ClientLoginModule  required
        password-stacking="useFirstPass"
        ;

    // Put your login modules that need jBoss here
};

other {
    // Put your login modules that work without jBoss here

    // jBoss LoginModule
    org.jboss.security.ClientLoginModule  required;

    // Put your login modules that need jBoss here
};

Note

The configuration named 'other' is used by JAAS whenever it can't find an entry matching the name passed to the LoginContext constructor.

The Beans and Servlet

We have now touched on all of the JBoss security related elements we need to configure to secure the deployment of EJBs and web applications. Let's now put together two simple session beans and a servlet that we will secure to demonstrate the use what we have gone over.

The following figures give the code listings for the the home, remote and bean classes for the simple stateless and stateful session beans we are going to secure, along with a simple client that accesses instances of the session beans. Also shown is a simple servlet that accesses one of the EJBs. The complete source code along with deployment descriptors and an Ant build script is available JAAS-Howto Files.

Figure 11.12. The Session Beans Remote Interface

import javax.ejb.*;
import java.rmi.*;

public interface Session extends EJBObject
{
    public String echo(String arg) throws RemoteException;
    public void noop() throws RemoteException;
}

Figure 11.13. The Session Beans Home Interface

import javax.ejb.*;
import java.rmi.*;

public interface SessionHome extends EJBHome
{
    public Session create() throws RemoteException, CreateException;
}

Figure 11.14. The Stateless Session Bean

import java.rmi.RemoteException;
import java.security.Principal;
import javax.ejb.*;

/**
@ejbHome: SessionHome
@ejbRemote: Session
*/
public class StatelessSessionBean implements SessionBean
{
    private SessionContext sessionContext;

    public void ejbCreate() throws CreateException
    {
        System.out.println("StatelessSessionBean.ejbCreate() called");
    }

    public void ejbActivate()
    {
        System.out.println("StatelessSessionBean.ejbActivate() called");
    }

    public void ejbPassivate()
    {
        System.out.println("StatelessSessionBean.ejbPassivate() called");
    }

    public void ejbRemove()
    {
        System.out.println("StatelessSessionBean.ejbRemove() called");
    }

    public void setSessionContext(SessionContext context)
    {
        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;
    }
    public void noop()
    {
        System.out.println("StatelessSessionBean.noop");
        Principal p = sessionContext.getCallerPrincipal();
        System.out.println("StatelessSessionBean.noop, callerPrincipal="+p);
    }
}

Figure 11.15. The Stateful Session Bean

import java.rmi.RemoteException;
import java.security.Principal;
import javax.ejb.*;

/**
@ejbHome: SessionHome
@ejbRemote: Session
*/
public class StatefulSessionBean implements SessionBean
{
    private SessionContext sessionContext;

    public void ejbCreate() throws CreateException
    {
        System.out.println("StatefulSessionBean.ejbCreate() called");
    }

    public void ejbActivate() 
    {
        System.out.println("StatefulSessionBean.ejbActivate() called");
    }

    public void ejbPassivate() 
    {
        System.out.println("StatefulSessionBean.ejbPassivate() called");
    }

    public void ejbRemove() 
    {
        System.out.println("StatefulSessionBean.ejbRemove() called");
    }

    public void setSessionContext(SessionContext context) 
    {
        sessionContext = context;
    }

    public String echo(String arg)
    {
        System.out.println("StatefulSessionBean.echo, arg="+arg);
        Principal p = sessionContext.getCallerPrincipal();
        System.out.println("StatefulSessionBean.echo, callerPrincipal="+p);
        return arg;
    }
    public void noop() 
    {
        System.out.println("StatefulSessionBean.noop");
        Principal p = sessionContext.getCallerPrincipal();
        System.out.println("StatefulSessionBean.noop, callerPrincipal="+p);
    }
}

Figure 11.16. The ejb-jar Deployment Descriptor

<?xml version="1.0" encoding="UTF-8"?>
<!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 stateless session echo bean</description>
            <ejb-name>StatelessSession</ejb-name>
            <home>SessionHome</home>
            <remote>Session</remote>
            <ejb-class>StatelessSessionBean</ejb-class>
            <session-type>Stateless</session-type>
            <transaction-type>Container</transaction-type>
        </session>

        <session>
            <description>A trival stateful session echo bean</description>
            <ejb-name>StatefulSession</ejb-name>
            <home>SessionHome</home>
            <remote>Session</remote>
            <ejb-class>StatefulSessionBean</ejb-class>
            <session-type>Stateful</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>
                <ejb-name>StatefulSession</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>

        <method-permission>
            <role-name>Coder</role-name>
            <method>
                <ejb-name>StatefulSession</ejb-name>
                <method-name>create</method-name>
            </method>
            <method>
                <ejb-name>StatefulSession</ejb-name>
                <method-name>remove</method-name>
            </method>
            <method>
                <ejb-name>StatefulSession</ejb-name>
                <method-name>noop</method-name>
            </method>
        </method-permission>
    </assembly-descriptor>
</ejb-jar>

Figure 11.17. The jboss.xml Deployment Descriptor

<?xml version="1.0" encoding="UTF-8"?>
<jboss>
    <!-- All bean containers use this security manager by default -->
    <security-domain>java:/jaas/example1</security-domain>

    <enterprise-beans>
        <session>
            <ejb-name>StatelessSession</ejb-name>
            <jndi-name>example1/StatelessSession</jndi-name>
        </session>

        <session>
            <ejb-name>StatefulSession</ejb-name>
            <jndi-name>example1/StatefulSession</jndi-name>
        </session>
    </enterprise-beans>
</jboss>

Figure 11.18. The Client

import java.io.IOException;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;

/** A simple session client that access the two secured EJBs as the user
passed in on the command line.

@author Scott_Stark@displayscape.com
@version $Revision: 3.0 $
*/
public class SessionClient
{
    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
    {
        if( args.length != 3 )
            throw new IllegalArgumentException("Usage: username password example");

        String name = args[0];
        char[] password = args[1].toCharArray();
        String example = args[2];
        System.out.println("+++ Running SessionClient with username="+name+", password="+args[1]+", example="+example);
        try
        {
            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 iniContext = new InitialContext();
            SessionHome home = (SessionHome) iniContext.lookup(example+"/StatelessSession");
            System.out.println("Found StatelessSessionHome");
            Session bean = home.create();
            System.out.println("Created StatelessSession");
            System.out.println("Bean.echo('Hello') -> "+bean.echo("Hello"));
            bean.remove();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        try
        {
            InitialContext iniContext = new InitialContext();
            SessionHome home = (SessionHome) iniContext.lookup(example+"/StatefulSession");
            System.out.println("Found StatefulSessionHome");
            Session bean = home.create();
            System.out.println("Created StatefulSession");
            bean.noop();
            System.out.println("Bean.noop() called");
            System.out.println("Bean.echo('Hello') -> "+bean.echo("Hello"));
            bean.remove();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

Figure 11.19. The Secure Servlet

import java.io.IOException;
import java.io.PrintWriter;
import java.security.Principal;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Session;
import SessionHome;

/** A simple servlet that accesses the stateless session bean.
@author Scott_Stark@displayscape.com
*/
public class SecureEJBServlet extends HttpServlet
{
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String echoMsg = null;
        try
        {
            InitialContext ctx = new InitialContext();
            SessionHome home = (SessionHome) ctx.lookup("java:comp/env/ejb/SecuredEJB");
            Session bean = home.create();
            echoMsg = bean.echo("Hello");
        }
        catch(Exception e)
        {
            throw new ServletException("Failed to call SecuredEJB.echo", e);
        }
        Principal user = request.getUserPrincipal();
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>SecureEJBServlet</title></head>");
        out.println("<h1>SecureServlet Accessed</h1>");
        out.println("<body><pre>You have accessed this servlet as user: "+user);
        out.println("The SecuredEJB.echo('Hello') returned: "+echoMsg);
        out.println("</pre></body></html>");
        out.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        processRequest(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        processRequest(request, response);
    }
}

Figure 11.20. The web-app Deployment Descriptor

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>
<!-- ### Servlets -->
    <servlet>
        <servlet-name>SecureServlet</servlet-name>
        <servlet-class>SecureEJBServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SecureServlet</servlet-name>
        <url-pattern>/restricted/SecureServlet</url-pattern>
    </servlet-mapping>

<!-- ### Security -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Restricted</web-resource-name>
            <description>Declarative security tests</description>
            <url-pattern>/restricted/*</url-pattern>
            <http-method>HEAD</http-method>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>PUT</http-method>
            <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>Echo</role-name>
        </auth-constraint>
        <user-data-constraint>
            <description>no description</description>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>JAAS Tutorial Servlets</realm-name>
    </login-config>

    <security-role>
        <description>A user allowed to invoke echo methods</description>
        <role-name>Echo</role-name>
    </security-role>

<!-- ### EJB References (java:comp/env/ejb) -->
    <ejb-ref>
        <ejb-ref-name>ejb/SecuredEJB</ejb-ref-name>
        <ejb-ref-type>Session</ejb-ref-type>
        <home>SessionHome</home>
        <remote>Session</remote>
    </ejb-ref>

</web-app>

Figure 11.21. The jboss-web.xml Deployment Descriptor

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <security-domain>java:/jaas/example1</security-domain>

    <ejb-ref>
        <ejb-ref-name>ejb/SecuredEJB</ejb-ref-name>
        <jndi-name>example1/StatelessSession</jndi-name>
    </ejb-ref>
</jboss-web>

The session beans are trivial and both the stateless and stateful bean use the same home and remote interfaces. 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. The servlet looks up the home interface of the StatelessSession bean and creates a Session instance. It then invokes echo on this and displays the user principal that accessed the servlet and the output of the Session.echo method. Now, finally let's put everything together and deploy the session beans and servlet as a J2EE ear.

Deploying a Secured J2EE Ear

This section details the procedure for building, deploying and testing a secured J2EE ear made up of the EJBs and servlet shown in the preceeding figures. I'll walk you through the build of two different versions of the ears using Ant and the build.xml file contained in the tutorial files bundle. The files bundle is available from JAAS-Howto Files.

Procedure 11.3. Deployment Steps

  1. Download the JBoss/Tomcat bundle. Go toJBoss @ SourceForgeand download the JBoss-2.2.2/Tomcat-3.2.2 bundle or later version. The 2.2.2 version of JBoss is the first to ship with the integrated ejb/web security that is required for this tutorial example. Unpack the archive to create a JBoss-2.2.2_Tomcat-3.2.2 directory that contains jboss and tomcat subdirectories. I have downloaded and unpacked the archive into /tmp on my machine so the directory structure looks like that shown in Figure 11.22. The jboss.dist and servlet.jar labels in red will be used below to configure the Ant build script.

    Figure 11.22. JBoss-2.2.2 Tomcat-3.2.2 Bundle Structure

  2. Download the tutorial files bundle. If you have not already, download the tutorial files from here: JAAS-Howto Files. Unpack the archive to create a jaas directory with the contents shown in Figure 11.23.

    Figure 11.23. JAAS Tutorial Files

  3. Build and deploy the tutorial ears.

    1. Edit the build.xml to set the correct location of the jboss.dist and servlet.jar variables to the corresponding paths of your JBoss/Tomcat bundle distribution installation. See Figure 11.22for the items in the distribution to which jboss.dist and servlet.jar refer. For my installation of the distribution under /tmp as shown above, the correct settings are given in Figure 11.24.

      Figure 11.24. JAAS Tutorial Files

      <project name="JAAS Howto Build Script" default="ears" basedir=".">
      ...
          <!-- Override with your JBoss server dist location -->
          <property name="jboss.dist" value="/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss"/>
          <!-- Override with your web server servlet jar location -->
          <property name="servlet.jar" value="/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar"/>
      

    2. Download and install Ant1.3 or later. If you don't have a copy of Ant on your system download version 1.3 or later from the Apache site here.

    3. Build and install the tutorial ears. From within the jaas directory that contains the Ant build.xml file, execute the following command:

          
      bash-2.04$ ant ears
      Searching for build.xml ...
      Buildfile: /tmp/jaas/build.xml

      validate:

      fail_if_not_valid:

      init:
      Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
      Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

      compile:
          [mkdir] Created dir: /tmp/jaas/build/classes
          [javac] Compiling 7 source files to /tmp/jaas/build/classes

      jar1:
          [mkdir] Created dir: /tmp/jaas/build/META-INF
           [copy] Copying 1 files to /tmp/jaas/build/META-INF
           [copy] Copying 1 files to /tmp/jaas/build/META-INF
            [jar] Building jar: /tmp/jaas/build/ssbean1.jar

      war1:
          [mkdir] Created dir: /tmp/jaas/build/WEB-INF/classes
           [copy] Copying 1 files to /tmp/jaas/build/WEB-INF
           [copy] Copying 1 files to /tmp/jaas/build/WEB-INF
           [copy] Copying 1 files to /tmp/jaas/build/WEB-INF/classes
            [jar] Building jar: /tmp/jaas/build/tutorial1.war

      ear1:
           [copy] Copying 1 files to /tmp/jaas/build/META-INF
            [jar] Building jar: /tmp/jaas/build/tutorial1.ear

      jar2:
         [delete] Deleting directory /tmp/jaas/build/META-INF
          [mkdir] Created dir: /tmp/jaas/build/META-INF
           [copy] Copying 1 files to /tmp/jaas/build/META-INF
           [copy] Copying 1 files to /tmp/jaas/build/META-INF
            [jar] Building jar: /tmp/jaas/build/ssbean2.jar

      war2:
         [delete] Deleting directory /tmp/jaas/build/WEB-INF
          [mkdir] Created dir: /tmp/jaas/build/WEB-INF/classes
           [copy] Copying 1 files to /tmp/jaas/build/WEB-INF
           [copy] Copying 1 files to /tmp/jaas/build/WEB-INF
           [copy] Copying 1 files to /tmp/jaas/build/WEB-INF/classes
            [jar] Building jar: /tmp/jaas/build/tutorial2.war

      ear2:
           [copy] Copying 1 files to /tmp/jaas/build/META-INF
            [jar] Building jar: /tmp/jaas/build/tutorial2.ear

      ears:
           [copy] Copying 1 files to /tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy
           [copy] Copying 1 files to /tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy
           [copy] Copying 1 files to /tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/conf/tomcat

      BUILD SUCCESSFUL

      Total time: 8 seconds

      This has created two ears(tutorial1.ear, tutorial2.ear) that contain the ejbs and servlet presented earlier and placed them into the JBoss server deploy directory. It has also replaced the contents of the JBoss server auth.conf file with the jaas/server_auth.conf file contents. This file is the same as that show in Figure 11.10.

  4. Start the JBoss/Tomcat server. Go to the jboss/bin directory of your JBoss/Tomcat bundle installation and execute the run_with_tomcat.sh for *nix platforms and run_with_tomcat.bat for w32 platforms. A number of messages will be displayed on your console. Some of the key messages you should see are:

        
    bash-2.04$ run_with_tomcat.sh
    JBOSS_CLASSPATH=:/lib/tools.jar:run.jar:../lib/crimson.jar
    jboss.home = /tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
    Using JAAS LoginConfig: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/conf/tomcat/auth.conf
    Using configuration "tomcat"
    ...
    [EmbeddedTomcatSX] Starting
    [EmbeddedTomcatSX] Starting EmbeddedTomcatSX....
    2001-05-25 08:45:45 - ContextManager: Adding context Ctx( /examples )
    2001-05-25 08:45:45 - ContextManager: Adding context Ctx( /admin )
    [EmbeddedTomcatSX] Starting tomcat. Check logs/tomcat.log for error messages 
    2001-05-25 08:45:46 - ContextManager: Adding context Ctx(  )
    2001-05-25 08:45:47 - ContextManager: Adding context Ctx( /test )
    2001-05-25 08:45:49 - PoolTcpConnector: Starting HttpConnectionHandler on 8080
    [EmbeddedTomcatSX] OK
    [EmbeddedTomcatSX] Started
    ...
    [J2EE Deployer Default] Deploy J2EE application: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy/tutorial1.ear
    [J2EE Deployer Default] Create application tutorial1.ear
    [J2EE Deployer Default] inflate and install module tutorial1.war
    [J2EE Deployer Default] install module ssbean1.jar
    [J2EE Deployer Default] add all ejb jar files to the common classpath
    [Container factory] Deploying:file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial1.ear
    [Verifier] Verifying file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial1.ear/ejb1004.jar
    [Container factory] Deploying StatelessSession
    [Container factory] lookup securityManager name: java:/jaas/example1
    [Container factory] JAAS.Created securityMgr=org.jboss.security.plugins.JaasSecurityManager@114460
    [Container factory] JAAS.setCachePolicy, c=null
    [Container factory] JAAS.Added example1, org.jboss.security.plugins.JaasSecurityManager@114460 to map
    [Container factory] Deploying StatefulSession
    [Container factory] lookup securityManager name: java:/jaas/example1
    [Bean Cache] Cache policy scheduler started
    [Container factory] Deployed application: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial1.ear
    [J2EE Deployer Default] Starting module tutorial1.war
    [Auto deploy] deploy, ctxPath=/jaas-example1, warUrl=file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial1.ear/web1003/
    2001-05-25 08:45:55 - ContextManager: Adding context Ctx( /jaas-example1 )
    [Auto deploy] AbstractWebContainer.parseWebAppDescriptors, Begin
    [Auto deploy] addEnvEntries
    [Auto deploy] linkResourceRefs
    [Auto deploy] linkEjbRefs
    [Auto deploy] Linking ejb-ref: ejb/SecuredEJB to JNDI name: example1/StatelessSession
    [Auto deploy] linkSecurityDomain
    [Auto deploy] Linking security/securityMgr to JNDI name: java:/jaas/example1
    [Auto deploy] AbstractWebContainer.parseWebAppDescriptors, End
    [Auto deploy] Initialized: {WebApplication: /tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial1.ear/web1003/, URL: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial1.ear/web1003/, classLoader: AdaptiveClassLoader(  ):5909743}
    [J2EE Deployer Default] J2EE application: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy/tutorial1.ear is deployed.
    [Auto deploy] Auto deploy of file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy/tutorial2.ear
    [J2EE Deployer Default] Deploy J2EE application: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy/tutorial2.ear
    [J2EE Deployer Default] Create application tutorial2.ear
    [J2EE Deployer Default] inflate and install module tutorial2.war
    [J2EE Deployer Default] install module ssbean2.jar
    [J2EE Deployer Default] add all ejb jar files to the common classpath
    [Container factory] Deploying:file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial2.ear
    [Verifier] Verifying file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial2.ear/ejb1006.jar
    [Container factory] Deploying StatelessSession
    [Container factory] lookup securityManager name: java:/jaas/example2
    [Container factory] JAAS.Created securityMgr=org.jboss.security.plugins.JaasSecurityManager@3ac93e
    [Container factory] JAAS.setCachePolicy, c=null
    [Container factory] JAAS.Added example2, org.jboss.security.plugins.JaasSecurityManager@3ac93e to map
    [Container factory] Deploying StatefulSession
    [Container factory] lookup securityManager name: java:/jaas/example2
    [Container factory] Deployed application: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial2.ear
    [J2EE Deployer Default] Starting module tutorial2.war
    [Auto deploy] deploy, ctxPath=/jaas-example2, warUrl=file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial2.ear/web1005/
    2001-05-25 08:45:57 - ContextManager: Adding context Ctx( /jaas-example2 )
    [Auto deploy] AbstractWebContainer.parseWebAppDescriptors, Begin
    [Auto deploy] addEnvEntries
    [Auto deploy] linkResourceRefs
    [Auto deploy] linkEjbRefs
    [Auto deploy] Linking ejb-ref: ejb/SecuredEJB to JNDI name: example2/StatelessSession
    [Auto deploy] linkSecurityDomain
    [Auto deploy] Linking security/securityMgr to JNDI name: java:/jaas/example2
    [Auto deploy] AbstractWebContainer.parseWebAppDescriptors, End
    [Auto deploy] Initialized: {WebApplication: /tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial2.ear/web1005/, URL: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/tmp/deploy/Default/tutorial2.ear/web1005/, classLoader: AdaptiveClassLoader(  ):86840}
    [J2EE Deployer Default] J2EE application: file:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/deploy/tutorial2.ear is deployed.
    ...
    [Service Control] Started 25 services
    [Default] JBoss 2.2.2 Started in 0m:19s

    At this point you have the JBoss server running with the embeded Tomcat servlet engine and the two tutorial ears have been deployed.

  5. Test access to the session bean

    At this point the session beans are deployed and only users with a role of 'Echo' are allowed to execute all home and remote interface methods of the StatelessSession and StatefulSession beans. There is 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' with roles of 'Java' and 'Coder' that should not be able to execute any methods of the StatelessSession because he does not have the required 'Echo' role. He should be able to create and remove StatefulSession beans and execute the noop method since the role of 'Coder' is allowed these operations. Lets run through 4 test cases that confirm we have properly secured the EJBs and servlet using the UsersRolesLoginModule.

    1. Run the SessionClient as user 'scott' with password 'echoman'. Do this by executing the following command:



      bash-2.04$ ant example1-test0
      Searching for build.xml ...
      Buildfile: /tmp/jaas/build.xml

      example1-test0:

      validate:

      fail_if_not_valid:

      init:
      Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
      Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

      compile:

      client-test0:
           [java] +++ Running SessionClient with username=scott, password=echoman, example=example1
           [java] Created LoginContext
           [java] Found StatelessSessionHome
           [java] Created StatelessSession
           [java] Bean.echo('Hello') -> Hello
           [java] Found StatefulSessionHome
           [java] Created StatefulSession
           [java] Bean.noop() called
           [java] Bean.echo('Hello') -> Hello

      BUILD SUCCESSFUL

      Total time: 2 seconds

      --- Server console:

      User 'scott' authenticated.
      [StatelessSession] StatelessSessionBean.ejbCreate() called
      [StatelessSession] StatelessSessionBean.echo, arg=Hello
      [StatelessSession] StatelessSessionBean.echo, callerPrincipal=caller_scott
      [StatefulSession] StatefulSessionBean.ejbCreate() called
      [StatefulSession] StatefulSessionBean.noop
      [StatefulSession] StatefulSessionBean.noop, callerPrincipal=caller_scott
      [StatefulSession] StatefulSessionBean.echo, arg=Hello
      [StatefulSession] StatefulSessionBean.echo, callerPrincipal=caller_scott
      [StatefulSession] StatefulSessionBean.ejbRemove() called

    2. Ok, so that succeeded as desired. Now we need to make sure that unauthorized users are actually denied access. This time run as username 'stark' with password 'javaman' by executing the following command:



      bash-2.04$ ant example1-test1
      Searching for build.xml ...
      Buildfile: /tmp/jaas/build.xml

      example1-test1:

      validate:

      fail_if_not_valid:

      init:
      Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
      Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

      compile:

      client-test1:
           [java] +++ Running SessionClient with username=stark, password=javaman, example=example1
           [java] Created LoginContext
           [java] Found StatelessSessionHome
           [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
           [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
           [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
           [java] java.lang.SecurityException: Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
           [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
           [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
           [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
           [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
           [java]     at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:248)
           [java]     at $Proxy0.create(Unknown Source)
           [java]     at SessionClient.main(SessionClient.java:77)
           [java] Found StatefulSessionHome
           [java] Created StatefulSession
           [java] Bean.noop() called
           [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
           [java]     javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]; nested exception is: 
           [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
           [java] javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]; nested exception is: 
           [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
           [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
           [java] java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
           [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
           [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
           [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
           [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invoke(Unknown Source)
           [java]     at org.jboss.ejb.plugins.jrmp.interfaces.StatefulSessionProxy.invoke(StatefulSessionProxy.java:186)
           [java]     at $Proxy1.echo(Unknown Source)
           [java]     at SessionClient.main(SessionClient.java:96)

      BUILD SUCCESSFUL

      Total time: 2 seconds

      --- Server console:

      User 'stark' authenticated.
      [StatelessSession] Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
      [StatefulSession] StatefulSessionBean.ejbCreate() called
      [StatefulSession] StatefulSessionBean.noop
      [StatefulSession] StatefulSessionBean.noop, callerPrincipal=caller_stark
      [StatefulSession] Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
      [StatefulSession] TRANSACTION ROLLBACK EXCEPTION:checkSecurityAssociation; nested exception is: 
              java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]; nested exception is: 
              java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
              java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
      [StatefulSession] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
      [StatefulSession]       java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
      [StatefulSession] java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
      [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:232)
      [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:169)
      [StatefulSession]       at org.jboss.ejb.plugins.StatefulSessionInstanceInterceptor.invoke(StatefulSessionInstanceInterceptor.java:209)
      [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invokeNext(TxInterceptorCMT.java:133)
      [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:263)
      [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:99)
      [StatefulSession]       at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:195)
      [StatefulSession]       at org.jboss.ejb.StatefulSessionContainer.invoke(StatefulSessionContainer.java:326)
      [StatefulSession]       at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invoke(JRMPContainerInvoker.java:392)
      [StatefulSession]       at java.lang.reflect.Method.invoke(Native Method)
      [StatefulSession]       at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
      [StatefulSession]       at sun.rmi.transport.Transport$1.run(Transport.java:152)
      [StatefulSession]       at java.security.AccessController.doPrivileged(Native Method)
      [StatefulSession]       at sun.rmi.transport.Transport.serviceCall(Transport.java:148)
      [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:465)
      [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:706)
      [StatefulSession]       at java.lang.Thread.run(Thread.java:484)

    3. This demonstrates that user 'stark' cannot execute any methods of the StatelessSession home or remote interfaces. Also demonstrated is that 'stark' can create a StatefulSession bean and invoke the noop method on it, but he cannot invoke the echo method which is correct. Now let's try username 'scott' with an invalid password to see if authentication fails:



      bash-2.04$ ant example1-test2
      Searching for build.xml ...
      Buildfile: /tmp/jaas/build.xml

      example1-test2:

      validate:

      fail_if_not_valid:

      init:
      Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
      Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

      compile:

      client-test2:
           [java] +++ Running SessionClient with username=scott, password=badpass, example=example1
           [java] Created LoginContext
           [java] Found StatelessSessionHome
           [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
           [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott
           [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott
           [java] java.lang.SecurityException: Authentication exception, principal=scott
           [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
           [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
           [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
           [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
           [java]     at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:248)
           [java]     at $Proxy0.create(Unknown Source)
           [java]     at SessionClient.main(SessionClient.java:77)
           [java] Found StatefulSessionHome
           [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
           [java]     javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott; nested exception is: 
           [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott
           [java] javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott; nested exception is: 
           [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott
           [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
           [java]     java.lang.SecurityException: Authentication exception, principal=scott
           [java] java.lang.SecurityException: Authentication exception, principal=scott
           [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
           [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
           [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
           [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
           [java]     at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:248)
           [java]     at $Proxy0.create(Unknown Source)
           [java]     at SessionClient.main(SessionClient.java:92)

      BUILD SUCCESSFUL

      Total time: 2 seconds

      --- Server console:

      [StatelessSession] Bad password for username=scott
      [StatelessSession] LoginException: Password Incorrect/Password Required
      [StatelessSession] Authentication exception, principal=scott
      [StatefulSession] Bad password for username=scott
      [StatefulSession] LoginException: Password Incorrect/Password Required
      [StatefulSession] Authentication exception, principal=scott
      [StatefulSession] TRANSACTION ROLLBACK EXCEPTION:checkSecurityAssociation; nested exception is: 
              java.lang.SecurityException: Authentication exception, principal=scott; nested exception is: 
              java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
              java.lang.SecurityException: Authentication exception, principal=scott
      [StatefulSession] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
      [StatefulSession]       java.lang.SecurityException: Authentication exception, principal=scott
      [StatefulSession] java.lang.SecurityException: Authentication exception, principal=scott
      [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:213)
      [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:144)
      [StatefulSession]       at org.jboss.ejb.plugins.StatefulSessionInstanceInterceptor.invokeHome(StatefulSessionInstanceInterceptor.java:99)
      [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invokeNext(TxInterceptorCMT.java:135)
      [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:263)
      [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invokeHome(TxInterceptorCMT.java:86)
      [StatefulSession]       at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:106)
      [StatefulSession]       at org.jboss.ejb.StatefulSessionContainer.invokeHome(StatefulSessionContainer.java:311)
      [StatefulSession]       at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invokeHome(JRMPContainerInvoker.java:369)
      [StatefulSession]       at java.lang.reflect.Method.invoke(Native Method)
      [StatefulSession]       at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
      [StatefulSession]       at sun.rmi.transport.Transport$1.run(Transport.java:152)
      [StatefulSession]       at java.security.AccessController.doPrivileged(Native Method)
      [StatefulSession]       at sun.rmi.transport.Transport.serviceCall(Transport.java:148)
      [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:465)
      [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:706)
      [StatefulSession]       at java.lang.Thread.run(Thread.java:484)

    4. All is working as expected using a Java client. Now we need to try to access the secure servlet and see that the same username and password we have been using to access the EJBs allows us access to the servlet. Since the servlet also calls the EJBs, we will see if the servlet user principal and credentials are passed correctly to the EJB. To try this, click on the this link: CLICK HEREand enter scott as the username and echoman as the password. You should see a page like that in the following figure.

      Figure 11.25. http://localhost:8080/jaas-example1/restricted/SecureServlet Output

      The output on the server console due to the client accessing the web will look like the following:



      [EmbeddedTomcatSX] Bad password for username=null
      [EmbeddedTomcatSX] LoginException: Password Incorrect/Password Required
      [EmbeddedTomcatSX] User: null is NOT authenticated
      User 'scott' authenticated.
      [EmbeddedTomcatSX] User: scott is authenticated
      [EmbeddedTomcatSX] User: scott is authorized
      User 'scott' authenticated.
      [StatelessSession] StatelessSessionBean.ejbCreate() called
      [StatelessSession] StatelessSessionBean.echo, arg=Hello
      [StatelessSession] StatelessSessionBean.echo, callerPrincipal=caller_scott

      The first set of error messages result from the web container trying to authenticate a 'null' username meaning an anonymous user. The JBossSecurityMgrRealm passes the null username and password onto the JBoss security manager rather than simply returning an unauthorized error in case the configured security manager wishes to treat a null username as a special user.

Using the DatabaseServerLoginModule

We have tested authenticating users based on the simple UsersRolesLoginModule module that reads the users.properties and roles.properties files bundled in the deployed ejb jar. This is convient for development, but it is not realistic of a production environment. In this section we will run through the same tests as the preceeding section, but we will be authenticating the users against a JDBC database using the DatabaseServerLoginModule.

Procedure 11.4.

    1. Build the security database. The first thing we need to do is to create the database tables required by the DatabaseServerLoginModule. The DatabaseServerLoginModule expects a Principals table that contains the principal id to password mapping and a Roles table that contains the principal id to roles mapping. The psuedo-schema for the two tables is:

        Principals(PrincipalID text, Password text)
        Roles(PrincipalID text, Role text, RoleGroup text)
      The DatabaseServerLoginModule doesn't actually care about the real schema of the tables since you can specify the prepared statement sql to use for each query. If don't don't specify any options to the DatabaseServerLoginModule it does assume that there is a Principals and Roles table accessible via the DataSource located under the JNDI name java:/DefaultDS and it forms the default sql queries based on the above schema. We are going to create our security tables using a schema that equates to the default, but we pass in values for all of the DatabaseServerLoginModule options to illustrate that this is what you should use to map your existing database schema into the form of result sets expected by the DatabaseServerLoginModule.

      To create the security tables that contain the user and role data equivalent to that in the users and roles properties files, we need to run the BuildDatabase Java program. With the JBoss server still running, execute:



      bash-2.04$ ant buildDB
      Searching for build.xml ...
      Buildfile: /tmp/jaas/build.xml

      validate:

      fail_if_not_valid:

      init:
      Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
      Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

      compile:

      buildDB:
      Created Principals table, result=false
      INSERT INTO Principals VALUES ('scott', 'echoman'), result=false
      INSERT INTO Principals VALUES ('stark', 'javaman'), result=false
      Created Roles table, result=false
      INSERT INTO Roles VALUES ('scott', 'Echo', 'Roles'), result=false
      INSERT INTO Roles VALUES ('scott', 'caller_scott', 'CallerPrincipal'), result=false
      INSERT INTO Roles VALUES ('stark', 'Java', 'Roles'), result=false
      INSERT INTO Roles VALUES ('stark', 'Coder', 'Roles'), result=false
      INSERT INTO Roles VALUES ('stark', 'caller_stark', 'CallerPrincipal'), result=false

      This creates a Principals and Roles tables with the indicated values in the Hypersonic database that is configured as the default in the standard JBoss distribution.

    2. Run the SessionClient as user 'scott' with password 'echoman' using the DatabaseServerLoginModule to perform the authentication. Do this by executing the following command:



      bash-2.04$ ant example2-test0
      Searching for build.xml ...
      Buildfile: /tmp/jaas/build.xml

      example2-test0:

      validate:

      fail_if_not_valid:

      init:
      Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
      Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

      compile:

      client-test0:
           [java] +++ Running SessionClient with username=scott, password=echoman, example=example2
           [java] Created LoginContext
           [java] Found StatelessSessionHome
           [java] Created StatelessSession
           [java] Bean.echo('Hello') -> Hello
           [java] Found StatefulSessionHome
           [java] Created StatefulSession
           [java] Bean.noop() called
           [java] Bean.echo('Hello') -> Hello

      BUILD SUCCESSFUL

      Total time: 2 seconds

      --- Server console:

      User 'scott' authenticated.
      [StatelessSession] StatelessSessionBean.ejbCreate() called
      [StatelessSession] StatelessSessionBean.echo, arg=Hello
      [StatelessSession] StatelessSessionBean.echo, callerPrincipal=caller_scott
      [StatefulSession] StatefulSessionBean.ejbCreate() called
      [StatefulSession] StatefulSessionBean.noop
      [StatefulSession] StatefulSessionBean.noop, callerPrincipal=caller_scott
      [StatefulSession] StatefulSessionBean.echo, arg=Hello
      [StatefulSession] StatefulSessionBean.echo, callerPrincipal=caller_scott
      [StatefulSession] StatefulSessionBean.ejbRemove() called
                              
  1. This time run as username 'stark' with password 'javaman' by executing the following command:




    bash-2.04$ ant example2-test1
    Searching for build.xml ...
    Buildfile: /tmp/jaas/build.xml

    example2-test1:

    validate:

    fail_if_not_valid:

    init:
    Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
    Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

    compile:

    client-test1:
         [java] +++ Running SessionClient with username=stark, password=javaman, example=example2
         [java] Created LoginContext
         [java] Found StatelessSessionHome
         [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
         [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
         [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
         [java] java.lang.SecurityException: Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
         [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
         [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
         [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
         [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
         [java]     at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:248)
         [java]     at $Proxy0.create(Unknown Source)
         [java]     at SessionClient.main(SessionClient.java:77)
         [java] Found StatefulSessionHome
         [java] Created StatefulSession
         [java] Bean.noop() called
         [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
         [java]     javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]; nested exception is: 
         [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
         [java] javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]; nested exception is: 
         [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
         [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
         [java] java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
         [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
         [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
         [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
         [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invoke(Unknown Source)
         [java]     at org.jboss.ejb.plugins.jrmp.interfaces.StatefulSessionProxy.invoke(StatefulSessionProxy.java:186)
         [java]     at $Proxy1.echo(Unknown Source)
         [java]     at SessionClient.main(SessionClient.java:96)

    BUILD SUCCESSFUL

    Total time: 2 seconds

    --- Server console:

    User 'stark' authenticated.
    [StatelessSession] Insufficient method permissions, principal=stark, method=create, requiredRoles=[Echo]
    [StatefulSession] StatefulSessionBean.ejbCreate() called
    [StatefulSession] StatefulSessionBean.noop
    [StatefulSession] StatefulSessionBean.noop, callerPrincipal=caller_stark
    [StatefulSession] Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
    [StatefulSession] TRANSACTION ROLLBACK EXCEPTION:checkSecurityAssociation; nested exception is: 
            java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]; nested exception is: 
            java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
            java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
    [StatefulSession] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
    [StatefulSession]       java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
    [StatefulSession] java.lang.SecurityException: Insufficient method permissions, principal=stark, method=echo, requiredRoles=[Echo]
    [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:232)
    [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:169)
    [StatefulSession]       at org.jboss.ejb.plugins.StatefulSessionInstanceInterceptor.invoke(StatefulSessionInstanceInterceptor.java:209)
    [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invokeNext(TxInterceptorCMT.java:133)
    [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:263)
    [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:99)
    [StatefulSession]       at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:195)
    [StatefulSession]       at org.jboss.ejb.StatefulSessionContainer.invoke(StatefulSessionContainer.java:326)
    [StatefulSession]       at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invoke(JRMPContainerInvoker.java:392)
    [StatefulSession]       at java.lang.reflect.Method.invoke(Native Method)
    [StatefulSession]       at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
    [StatefulSession]       at sun.rmi.transport.Transport$1.run(Transport.java:152)
    [StatefulSession]       at java.security.AccessController.doPrivileged(Native Method)
    [StatefulSession]       at sun.rmi.transport.Transport.serviceCall(Transport.java:148)
    [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:465)
    [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:706)
    [StatefulSession]       at java.lang.Thread.run(Thread.java:484)
                            
  2. As before, this demonstrates that user 'stark' cannot execute any methods of the StatelessSession home or remote interfaces. Also demonstrated is that 'stark' can create a StatefulSession bean and invoke the noop method on it, but he cannot invoke the echo method which is correct. Now let's try username 'scott' with an invalid password to see if authentication fails:



    bash-2.04$ ant example2-test2
    Searching for build.xml ...
    Buildfile: /tmp/jaas/build.xml

    example2-test2:

    validate:

    fail_if_not_valid:

    init:
    Using jboss.dist=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss
    Using classpath=/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/ejb.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jaas.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jbosssx-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jboss-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/jboss/client/jnp-client.jar:/tmp/JBoss-2.2.2_Tomcat-3.2.2/tomcat/lib/servlet.jar:/tmp/jaas/build/classes

    compile:

    client-test2:
         [java] +++ Running SessionClient with username=scott, password=badpass, example=example2
         [java] Created LoginContext
         [java] Found StatelessSessionHome
         [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
         [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott
         [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott
         [java] java.lang.SecurityException: Authentication exception, principal=scott
         [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
         [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
         [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
         [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
         [java]     at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:248)
         [java]     at $Proxy0.create(Unknown Source)
         [java]     at SessionClient.main(SessionClient.java:77)
         [java] Found StatefulSessionHome
         [java] java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
         [java]     javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott; nested exception is: 
         [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott
         [java] javax.transaction.TransactionRolledbackException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott; nested exception is: 
         [java]     java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott
         [java] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
         [java]     java.lang.SecurityException: Authentication exception, principal=scott
         [java] java.lang.SecurityException: Authentication exception, principal=scott
         [java]     at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
         [java]     at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
         [java]     at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:122)
         [java]     at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker_Stub.invokeHome(Unknown Source)
         [java]     at org.jboss.ejb.plugins.jrmp.interfaces.HomeProxy.invoke(HomeProxy.java:248)
         [java]     at $Proxy0.create(Unknown Source)
         [java]     at SessionClient.main(SessionClient.java:92)

    BUILD SUCCESSFUL

    Total time: 2 seconds

    --- Server console:

    [StatelessSession] Bad password for username=scott
    [StatelessSession] LoginException: Password Incorrect/Password Required
    [StatelessSession] Authentication exception, principal=scott
    [StatefulSession] Bad password for username=scott
    [StatefulSession] LoginException: Password Incorrect/Password Required
    [StatefulSession] Authentication exception, principal=scott
    [StatefulSession] TRANSACTION ROLLBACK EXCEPTION:checkSecurityAssociation; nested exception is: 
            java.lang.SecurityException: Authentication exception, principal=scott; nested exception is: 
            java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
            java.lang.SecurityException: Authentication exception, principal=scott
    [StatefulSession] java.rmi.RemoteException: checkSecurityAssociation; nested exception is: 
    [StatefulSession]       java.lang.SecurityException: Authentication exception, principal=scott
    [StatefulSession] java.lang.SecurityException: Authentication exception, principal=scott
    [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:213)
    [StatefulSession]       at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:144)
    [StatefulSession]       at org.jboss.ejb.plugins.StatefulSessionInstanceInterceptor.invokeHome(StatefulSessionInstanceInterceptor.java:99)
    [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invokeNext(TxInterceptorCMT.java:135)
    [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:263)
    [StatefulSession]       at org.jboss.ejb.plugins.TxInterceptorCMT.invokeHome(TxInterceptorCMT.java:86)
    [StatefulSession]       at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:106)
    [StatefulSession]       at org.jboss.ejb.StatefulSessionContainer.invokeHome(StatefulSessionContainer.java:311)
    [StatefulSession]       at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invokeHome(JRMPContainerInvoker.java:369)
    [StatefulSession]       at java.lang.reflect.Method.invoke(Native Method)
    [StatefulSession]       at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
    [StatefulSession]       at sun.rmi.transport.Transport$1.run(Transport.java:152)
    [StatefulSession]       at java.security.AccessController.doPrivileged(Native Method)
    [StatefulSession]       at sun.rmi.transport.Transport.serviceCall(Transport.java:148)
    [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:465)
    [StatefulSession]       at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:706)
    [StatefulSession]       at java.lang.Thread.run(Thread.java:484)
                            

Wrap Up

This has been an introduction to the steps required to enable security for EJB and web containers using the JBossSX security implementation. Support for more advanced customization of security is documented in the JBossSX chapter.