JBoss at Work: A Practical Guide
9.10. EJB Security
To secure the EJB tier, we need to do the following:
9.10.1. JAAS Domain in jboss.xml
The security domain in jboss.xml defines a security domain used by all EJBs in the application. To join the global security domain for the entire JAW Motors application, the security domain must match the "JawJaasDbRealm" JAAS application name from login-config.xml and the <security-domain> element in jboss-web.xml. The <security-domain> element defines a single security domain used by all EJBs in the application. The <security-domain> element comes before the elements that define the JNDI-based resources. Example 9-13 shows the <security-domain> element in jboss.xml. Example 9-13. jboss.xml
<jboss> <security-domain>java:/jaas/JawJaasDbRealm</security-domain> ... </jboss>
The <security-domain> uses java:/jaas/JawJaasDbRealm because it is the JBoss-specific JNDI name used when JBoss deploys the LoginModule as a managed service. The pattern here is that JBoss prefixes its JAAS JNDI names with java:/jaas. 9.10.2. Automating JAAS Domain Settings in jboss.xml
If you recall from the Session Bean chapter, we used XDoclet's Ant <ejbdoclet> task and its <jboss> subtask to generate the J2EE standard ejb-jar.xml and jboss.xml JBoss-specific EJB deployment descriptors, respectively. We now add a securitydomain attribute to the <jboss> subtask in the ejb sub-project's build.xml (Example 9-14) to generate the <security-domain> element in jboss.xml. Example 9-14. ejb/build.xml
... <ejbdoclet> ... <jboss version="4.0" destdir="${gen.source.dir}" securitydomain="java:/jaas/JawJaasDbRealm"/> </ejbdoclet> ...
9.10.3. Protecting EJBs with ejb-jar.xml
J2EE provides Declarative Security, so we modify ejb-jar.xml in Example 9-15 to configure EJB security. Example 9-15. ejb-jar.xml
<enterprise-beans> <session> ... <ejb-name>InventoryFacade</ejb-name> ... </session> ... </enterprise-beans> ... <!-- Assembly Descriptor --> <assembly-descriptor> <security-role> <role-name>Manager</role-name> </security-role> <security-role> <role-name>guest</role-name> </security-role> ... <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>create</method-name> <method-params> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>create</method-name> <method-params> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>listAvailableCars</method-name> <method-params> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>listAvailableCars</method-name> <method-params> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>findCar</method-name> <method-params> <method-param>int</method-param> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>findCar</method-name> <method-params> <method-param>int</method-param> </method-params> </method> </method-permission> <method-permission> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>deleteCars</method-name> <method-params> <method-param>java.lang.String[ ]</method-param> </method-params> </method> </method-permission> <method-permission> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>deleteCars</method-name> <method-params> <method-param>java.lang.String[ ]</method-param> </method-params> </method> </method-permission> <method-permission> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>saveCar</method-name> <method-params> <method-param> com.jbossatwork.dto.CarDTO</method-param> </method-params> </method> </method-permission> <method-permission> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>saveCar</method-name> <method-params> <method-param>com.jbossatwork.dto.CarDTO</method-param> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>buyCar</method-name> <method-params> <method-param>int</method-param> <method-param>double</method-param>> </method-params> </method> </method-permission> <method-permission> <role-name>guest</role-name> <role-name>Manager</role-name> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>buyCar</method-name> <method-params> <method-param>int</method-param> <method-param>double</method-param> </method-params> </method> </method-permission> ... </assembly-descriptor>
As we did in web.xml, we're creating <security-role> elements for the guest and Manager security roles in the JAW Motors application. The <security-role-ref> elements define the security roles (guest, Manager) for the InventoryFacade Bean. The <method-permission> elements specify that:
These security settings in ejb-jar.xml ensure that someone with the Manager role has full access to both the secure and non-secure methods in InventoryFacadeBean, and that a guest user can only create the EJB and access its non-secure methods.
9.10.4. Automating EJB Security Settings with XDoclet
We already have an Ant-based build process that works with XDoclet, so we just have to add a couple of XDoclet tags to the InventoryFacadeBean in Example 9-16 so the Ant build process generates the new security settings in the ejb-jar.xml J2EE standard EJB deployment descriptor. Example 9-16. InventoryFacadeBean.java
/** * ... * * @ejb.security-role-ref * role-name="Manager" * role-link="Manager" * * @ejb.security-role-ref * role-name="guest" * role-link="guest" */ public class InventoryFacadeBean implements SessionBean { ... /** * @ejb.create-method * @ejb.permission * role-name="guest,Manager" * */ public void ejbCreate( ) throws CreateException { } ... /** * ... * @ejb.permission * role-name="guest,Manager" * */ public List listAvailableCars( ) throws EJBException { ... } ... /** * ... * @ejb.permission * role-name="guest,Manager" * */ public CarDTO findCar(int id) throws EJBException { ... } /** * ... * @ejb.permission * role-name="Manager" * */ public void deleteCars(String[ ] ids) throws EJBException { ... } /** * ... * @ejb.permission * role-name="Manager" * */ public void saveCar(CarDTO car) throws EJBException { ... } /** * ... * @ejb.permission * role-name="guest,Manager" * */ public void buyCar(int carId, double price) throws EJBException { ... } }
The class-level @ejb.security-role-ref XDoclet tags associate the InventoryFacade Bean with the Manager security role. The @ejb.permission tag on the ejbCreate( ), listAvailableCars( ), findCar( ), and buyCar( ) methods makes them accessible only to users in the guest and Manager roles. The @ejb.permission tag on the deleteCars( ) and saveCar( ) methods makes them accessible only to users in the Manager role. 9.10.5. Testing Secure EJB Methods
Now that we've propagated the correct user credentials and restricted access to the InventoryFacadeBean's secure methods, let's test our application to ensure that everything still works properly. Here are the steps you should follow to build and deploy the application:
Click on the "Modify Inventory" link on the JAW Motors home page and everything should work properly. |