All J2EE application servers are required to provide infrastructure to authenticate users and enforce authorization policies.
Authorization policies are specified declaratively using deployment
descriptors. Application servers usually provide custom tools to
help define authorization policies, or they can be defined manually by
typing out XML. Bean developers are not required to implement
security programmatically in their applications.
A Bean developer encapsulates business logic in an EJB and
specifies the security settings (such as which user is allowed to invoke
which method) in the deployment descriptor. The application server
authenticates each client and consults the deployment descriptor to see if the client is authorized to perform the given operation.
The following section shows you how to secure your Geocode EJB without using any application server-specific tool.
Here are the steps to follow:
1. Define a logical security realm for the EJB.
a. Define abstract security roles.
b. Define method permissions.
2. Map abstract security roles to users/groups in the application server security realm.
Unpack the Geocode EJB in a directory, for example, <temp>, using the following command:
jar -xvf myGeocodeEJB.jar <temp>
Open <temp>/META-INF/ejb-jar.xml in your favorite XML editor. All security-related declarations are specified in the <assembly-descriptor> element at the end of ejb-jar.xml.
Define an abstract security role, reverse-geocoders:
<ejb-jar>
<enterprise-beans>
...
</enterprise-beans>
<assembly-descriptor>
<security-role>
<description>Users who are allowed to reverse
geocode</description>
<role-name>reverse-geocoders</role-name>
</security-role>
</assembly-descriptor>
</ejb-jar>
Next, restrict access to reverseGeocode() only to the reverse-geocoders security role by defining a method permission:
<ejb-jar>
<enterpise-beans>
...
</enterprise-beans>
<assembly-descriptor>
<security-role>
...
</security-role>
<method-permission>
<description>Restricting access to reverseGeocode()
method</description>
<role-name>reverse-geocoders</role-name>
<method>
<ejb-name>GeocodeEJB</ejb-name> <!--Assuming the name of our EJB is GeocodeEJB -->
<method-name>reverseGeocode</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar>
This is all you need to do to define a logical security realm for the EJB.
This process of defining a logical security realm is specified by
the EJB specification and is portable across all application servers.
Finally, you need to map the abstract security roles in the
logical security realm to the actual users and groups in
the application server security realm. How this is accomplished varies with different application servers.
Most application servers have their own deployment descriptors, which
allow you to specify this mapping. Some others may require you to use
their custom tools. Sun Java System Application Server 8.1 allows you to specify this mapping in the
sun-ejb-jar.xml.
Open <temp>/META-INF/sun-ejb-jar.xml in your favorite XML editor
and add the <security-role-mapping> element as follows:
<sun-ejb-jar>
...
...
<security-role-mapping>
<role-name>reverse-geocoders</role-name> <!--
Defined in the logical security realm of EJB -->
<group-name>surveyors</group-name> <!-- Defined
in the operational security realm of application server -->
</security-role-mapping>
</sun-ejb-jar>
Now, jar up the
<temp> folder back into an EJB and deploy it to your server.
Unauthenticated clients and clients not belonging to the surveyors group
will encounter an exception while invoking reverseGeocode() on this EJB.
Here is how a stand-alone application can impersonate a user (Larry)
belonging to the surveyors group and invoke reverseGeocode():
public static void main(String args[]){
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,” com.sun.appserv.naming.S1ASCtxFactory”);
env.put(Context.PROVIDER_URL,”iiop://<server>:<port>/”);
env.put(Context.SECURITY_PRINCIPAL,”Larry”); //User that belongs to the 'surveyors' group
env.put(Context.SECURITY_CREDENTIALS,”password”);
InitialContext ctx = new InitialContext(env);
Object ref = ctx.lookup("<GEOCODE_EJB_JNDI_NAME>");
GeocodeEJBHome geocodeEJBHome = (GeocodeEJBHome) PortableRemoteObject.narrow(ref,
GeocodeEJBHome.class);
GeocodeEJB geocode = geocodeEJBHome.create();
...
...
geocode.reverseGeocode(...);
}