Thursday, February 16, 2012

EJB3 and JAAS Security

In this article, we will see how to implement JAAS security for a EJB3 Bean with JBOSS application sever, and how a stand alone client access the EJB in a secured manner. Here we will have a stateless session bean , configured in a secured domain named Trng. In the application server we need to configure a login module and the domain settings corresponding to the name. In the client side we will just configure login module, which will just pass the credentials to the server security domain. Below is the piece of code for bean class and remote interface.
@Stateless
@SecurityDomain("Trng")
@Remote(TestEjb.class)

public class TestEjbBean implements TestEjb{//,TimedObject{

@Resource
private SessionContext ctx;
@RolesAllowed(value="admin")
public String getMessage(String message) {
System.out.println("Message from client:: "+message+":");
System.out.println("CALLER IDENTITY"+ctx.getCallerPrincipal());
return "Hello "+message;
}
}
@Remote
public interface TestEjb extends java.rmi.Remote{
public String getMessage(String message);
}
In the above piece of code we have a method getMessage() which is secured and it can accessed only by the client having role admin. And you can notice how the security domain is configured for the bean. Using the @SecurityDomain("TRNG") it tells the bean that it belongs to the particular security domain. The security domain can be configured to use any loging module that the App server provides or the custom one which the user can write. In this example I am using a simple uersroleslogin module. Its configured within JBOSS_HOME/server/default/conf/login-config.xml. Following shows the configuration of the domain named Trng.
<application-policy name="Trng">
      <authentication>
        <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
          flag="required">
          <module-option name="usersProperties">props/users.properties</module-option>
          <module-option name="rolesProperties">props/roles.properties</module-option>
          <module-option name="unauthenticatedIdentity">anonymous</module-option>
        </login-module>
      </authentication>
    </application-policy>
Next we will create a standalone client , which will access the secured bean in a secured manner. For that we need to have a loginmodule in the client side, as dicussed earlier this login module should pass the credentials to the server side login module. Following is the configuration of loginmodule in the client side with file name auth.conf
clientlogin{org.jboss.security.ClientLoginModule required;
 };
Name of the login module is clientlogin. This has the login module ClientLoginModule passing the credentials to the server.
Following is the piece of code for the client that access the secured bean in seured manner.
public static void main(String[] args) throws NamingException, LoginException {
String authFile="D:/ui_rds1/temp/src/auth.conf";System.setProperty("java.security.auth.login.config", authFile);
LoginCallBackHandler lc=new LoginCallBackHandler("admin","admin");LoginContext loginContext=
new LoginContext("clientlogin",lc);
loginContext.login();
System.out.println("Logged In");
Properties p=new Properties();p.setProperty(
"java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory" );
p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" );p.setProperty(
"java.naming.provider.url", "hostname:jnpport" );
InitialContext ic=new InitialContext(p);System.
out.println("Inititial context created");
TestEjb testEjb=(TestEjb) ic.lookup("TestEjbBean/remote");System.
out.println("Got the reference of Remote Interface");
System.out.println("Resulte from EJB::->"+testEjb.getMessage("Meiyappan"));
loginContext.logout();
testEjb.getMessage("sdfdsf");
}
In the above client set the system property to have the value of the location of conf file, which contains the loginmodule. Then we need to have a callback handler in which we will pass the username and password to the LoginContext. Following is a simplecall back handler class.
public class LoginCallBackHandler implements CallbackHandler{
private String username;
/**
* Password which will be set in the PasswordCallback, when PasswordCallback is handled
*/ private String password;
/**
* Constructor
* @param username The username
* @param password The password
*/
public LoginCallBackHandler(String username, String password) {
this.username = username;
this.password = password;
}
/**
* @param callbacks Instances of Callbacks
* @throws IOException IOException
* @throws UnsupportedCallbackException If Callback is other than NameCallback or PasswordCallback
*/ public void handle(Callback callbacks[]) throws IOException, UnsupportedCallbackException {
for(int i = 0; i < callbacks.length; i++) {
if(callbacks[i] instanceof NameCallback) {
NameCallback nc = (NameCallback)callbacks[i];
nc.setName(username);
System.out.println("USER NAME:: "+username);}
else if(callbacks[i] instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback)callbacks[i];
pc.setPassword(password.toCharArray());
System.out.println("PASSWORD:: "+password);}
else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
}
}
}
}
Then we will create a logincontext with a name of the loginmodule which we gave in the .conf file and with the callbakhandler class instance, with the credentials set in the handler class. Now login using the login context. Next proceed with the normal lookup and access the method.For the specified username and password , we have the role admin defined in the roles.properties and users.properties file have this username and password. So the client will get the result from the bean. The logout from the logincontext and try to access the bean method again , you will be thrown with a EJBAcessException.
I hope this article is useful.