Skip to main content

Invoking secured remote EJB in JBoss 5

While preparing for SCBCD exam I encountered many difficulties in securing remote EJB 3 stateless session bean, and then calling such a component from remote standalone client. I hope this short introduction will help you… avoiding this problem. First, I simply put security annotations on my bean as follows:

@Stateless
@RolesAllowed("ROLE_ADMIN")
@PermitAll
public class DateServiceBean implements DateServiceRemote {
//...
}

Surprisingly such a bean can be easily deployed on JBoss 5.1.0 application server and all its methods are available. Why surprise? Because no ROLE_ADMIN was defined in JBoss nor the client was authorized. As the role was not defined, server silently ignored the security annotation! I learned my lesson – always verify security configuration, especially whether it actually secures anything…

Quick tour over JBoss documentation and I discovered @org.jboss.annotation.security.SecurityDomain annotation, which should mark all the beans using declarative as well as programmatic security, next to @RolesAllowed. This does not look well – not only this annotation needs to be repeated in every session bean (and what happens if you forget? – nothing, security is then ignored…), but it brings compile-time dependency on JBoss specific class! Fortunately, good-old-XML can be used:

<jboss>
<security-domain>dateserver</security-domain>
</jboss>


This short snippet in jboss.xml, which must be included in ejb-jar file, specifies the security domain name (dateserver) to be used by whole application. The same name should be referenced in login-config.xml file, located in JBoss distribution (typically server/default/conf directory):

<application-policy name="dateserver">
<authentication>
<login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
<module-option name="usersProperties">props/dateserver-users.properties</module-option>
<module-option name="rolesProperties">props/dateserver-roles.properties</module-option>
</login-module>
</authentication>


Just put this by the end of login-config.xml file and restart JBoss. Does describing dateserver-users.properties and dateserver-roles.properties is necessary? First consists of username=password items and second: username=role1, role2, role3 mappings. Now we have successfully secured our remote EJBs, which quick test from Java SE client proves:

javax.ejb.EJBAccessException: Caller unauthorized
at org.jboss.ejb3.security.RoleBasedAuthorizationInterceptorv2.invoke(RoleBasedAuthorizationInterceptorv2.java:199)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
[...]

Needless to say, the journey begins here... After few frustrating tries, I still couldn’t manage to log on application server and call secured method, using all varieties of standard lookup code:

Hashtable<String, String> properties = new Hashtable<String, String>();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.NamingContextFactory");
properties.put(Context.PROVIDER_URL, "jnp://localhost:1099");
properties.put(Context.URL_PKG_PREFIXES, "org.jnp.interfaces");
properties.put(Context.SECURITY_PRINCIPAL, "user");
properties.put(Context.SECURITY_CREDENTIALS, "password");
Context context = new InitialContext(properties);
DateServiceRemote dateService = (DateServiceRemote) context.lookup("DateServiceBean/remote");


JBoss totally ignores authentication data provided to the InitialContext, repeatedly returning "Caller unauthorized". Finally, I found out that this server since 5.0 version has its own, non-standard API for managing user authentication:

SecurityClient securityClient = SecurityClientFactory.getSecurityClient();
try {
securityClient.setSimple("username", "password");
securityClient.login();
//perform JNDI lookup and call business method as usual WITHOUT authentication
} finally {
securityClient.logout();
}


This is totally awful! – not only JBoss forces me to use container specific classes (org.jboss.security.client.SecurityClient), the API itself isn’t very well designed. Please note, that although SecurityClient instance is created, it is never passed to the InitialContext or propagated by any other way. This smells like nasty ThreadLocal and will behave unexpectedly if you forget to logout and reuse the thread (e.g. when pooling in client).
JBoss finally catches up usernames and validates them against password, discovers user roles and applies security to EJBs. But, IMHO, the price is very high. I could stand JBoss client libraries on my CLASSPATH, I somehow avoided vendor specific annotations. But all in all, some strange client class must be used. What’s the point of this whole standardization, JSRs and tons of specifications, if even the simplest use case cannot be implemented without making my hands dirty?

P.S.: I'm starting to blog in English. Comments are welcome, but please be gentle ;-).

Comments

  1. Hi, to authenticate a user over JNDI use:

    java.naming.factory.initial=org.jboss.security.jndi.JndiLoginInitialContextFactory

    This class was already in JBoss 3.

    Regards,
    Alexei.

    ReplyDelete
  2. I've tried JndiLoginInitialContextFactory (I forgot to mention it in post above, sorry), but it does not work on JBoss 5. See: JBoss forum

    ReplyDelete
  3. Hi,
    I'm facing the same problem. Have you found any solution?

    Regards,
    Luca

    ReplyDelete
  4. I actually solved the problem and everything is explained in the article.

    What problem exactly are you dealing with?

    ReplyDelete
  5. Hi!
    I have the same exception as you mentioned in this blog once I try to invoke secured bean (javax.ejb.EJBAccessException: Caller unauthorized) although I pass correct login and password.

    Can you give me some points how to you solved this problem?

    ReplyDelete
  6. Have you tried the solution I provided at the end of the article (SecurityClientFactory)? It worked for me.

    ReplyDelete
  7. Oh... Looks like I have missed last part of post.

    Thanks, that helps.

    ReplyDelete
  8. Very good article, the single place where I have found the way to authenticate and invoke a secured EJB.

    And IMHO, you are right, the way this authentication has to be done is awfull...

    Thank you!

    ReplyDelete
  9. Thanks mate, it helps ;)

    Anish Sneh

    ReplyDelete
  10. Thanks a million for that post. Definitely saved my day :-)

    Cheers,
    Jan

    ReplyDelete
  11. The 'securityClient.login()' does not do any authentication. It just saved the credential/principal to a client-side context. The authentication happens when client calls "context.lookup()".....

    ReplyDelete
  12. This comment has been removed by a blog administrator.

    ReplyDelete

Post a Comment