March 11, 2009

Caller identity propagation in JBoss

Recently I was asked by a client of mine how they would go about to propagate a JAAS Principle from one layer of their Java EE application to the next. Basically, the problem they had was that they needed to get some information about the logged on user from the Web Layer to the EJB Layer. Initially I was surprised because I thought that it is rather self-explanatory... apparently, if you haven't worked with JBoss, it isn't.

Anyway, if you should be stuck with the very same issue I will share a quick and easy solution for JBoss with you.

The security domain

First and foremost you have to create a security domain in JBoss' login-config.xml-file, which is located in your configuration's /conf directory.

For instance, add following snippet to the file:

<!-- defines a security domain for your jee application -->
<application-policy name="my-domain">
<authentication>
<!-- this login module is used for file based user/role-storage -->
<login-module flag="required" code="org.jboss.security.auth.spi.UsersRolesLoginModule">
<!-- these files must be present in {domain}/conf/prop -->

<!-- contains {user}={password} for each user -->
<module-option name="userProperties">prop/my-domain-user.properties</module-option>
<!-- contains {user}={role},... for each user -->
<module-option name="roleProperties">prop/my-domain-role.properties</module-option>
</login-module>
</authentication>
</application-policy>

Naturally you can use any other LoginModule. For the sake of argument, I used a simple one which uses files for users and their roles.

The ClientLoginModule

The security domain, however, is not quite enough. You also need to add an application policy for JBoss' ClientLoginModule, which, in turn, enables security context propagation between the application's layers.

If not already present (depending on your version of JBoss), also add this snippet to the file:

<!-- used by web layer to send login information to ejbs -->
<application-policy name="client-login">
<authentication>
<login-module flag="required" code="org.jboss.security.ClientLoginModule"/>
</authentication>
</application-policy>

Securing your application

Now that you have JBoss prepared, it's time to actually make your application use the security domain which we've just created. You can either do this in each individual EJB- or Web Archive contained in your EAR-file, however, in my example, I do it the easy way and define the security domain for the entire Enterprise Application.

In your EAR-file, you need to add a file called jboss-app.xml, which is placed in the META-INF directory.

If you haven't got a jboss-app.xml already, just copy and paste this into your newly created file:

<jboss-app>
<-- should point to the JAAS domain in the global JNDI -->
<security-domain>java:/jaas/my-domain</security-domain>
</jboss-app>

Propagating caller identity to the EJB

You are almost ready to go, however, the EJB invoked by your Web Layer will not yet be able to retrieve the caller's identity from the User Interface.

To get this working, add a ejb-jar.xml to your EJB archive's META-INF directory. You may or may not have this file. In most larger EJB 3.0-projects I've participated in, we eventually added one, even though it is not always necessary due to those nifty annotations.

Anyway, in the ejb-jar.xml, add the security-identity element to your bean's configuration, like so:

<ejb-jar>
<enterprise-beans>
<session>
<!-- replace MyBean with the name of _your_ bean -->
<ejb-name>MyBean</ejb-name>
<security-identity>
<use-caller-identity/>
</security-identity>
</session>
</enterprise-beans>
</ejb-jar>

Wow, finally done. Thanks to all those XML-snippets you are now able to share the Principal between your UI and the business layer.

To wrap this up I just want to add that it doesn't always make sense to have the same identity in your business logic, in many cases you run your code with a narrower collection of roles and the logged on user is irrelevant to the business logic. However, if you need an audit log in your database to see who did what, then this is a fairly convenient way to get your hands on the user name.

As always, enjoy!

9 comments:

Anonymous said...

Excellent work :)

Pether (www.cia.hack23.com)

aboelken said...

Thanks for the great post, but how do you access that Principal in the EJB layer?

Daniel Pfeifer said...

@aboelken:

Inject the SessionContext into your EJB, like this:

@Resource
private SessionContext ctx;

and then get it in your business methods like this:

ctx.getCallerPrincipal();

Hope that helps.

Bartłomiej Jończy said...

Thank you so much guys!

mbs said...

Did you try this with JBoss 7 AS or JBoss 6 EAP? We have some problems getting this to work...

Daniel Pfeifer said...

@mbs no, sorry. At the time of writing jboss 4.3 was still state of the art.

jay said...

Hi:

I'm trying to migrate to JBOSS 5.1.2 from Jboss 4.0.2.

I have packaged all my EJBs(2.x EJBs) are jar and not an ear.

I've created the security-domain & the client-login in my login-config.xml, and have included <
for all my EJBs (2.x)


But still the issue persits. I'm not seeing the security/caller identity being propagated from client login to invocation of EJBs. Any clue?

Since its a .jar file - I donot have jboss-app.xml.

Dominik said...

@jay:

Stumbled upon the same problem.

You should have a jboss.xml file in the ejb jars, put the tag in there.

If you don't have the jboss.xml in your EJB-jar create it in the META-INF folder.

pep grifell said...

thanks a lot !!!

that helped me from calling an EJB from another one (EJB 2.1) when migrating from JBoss4.2.3 to JBoss6.1.