The JBoss Application Server allows applications to be secured by declaring a security domain inside their deployment descriptor (jboss.xml or jboss-web.xml). The JBoss security component framework (JBossSX) is automatically invoked to perform security checks by intercepting EJB calls. JBossSX uses the JAAS framework in its default implementation.
Whereas WebLogic and WebSphere® return a valid JAAS Subject after a successful JAAS login, JBoss takes a different approach. Using JBoss terminology, a "client login" is a login from either a stand-alone Java™ client or an in-container servlet. In JBoss, upon a successful programmatic application JAAS client login, the returned Subject is not a "valid" one and cannot be reused by a different thread. In the JBoss model, client logins capture the caller's credentials and associate them with the current thread. The actual authentication is deferred until an attempt is made to access a server resource, such as an EJB. When a server resource is accessed, the credentials that were associated with the thread by the client LoginModule are used by the server LoginModule to perform the real authentication.
To associate the caller's credentials with the current calling thread, JBoss uses two built-in LoginModules: ClientLoginModule and AltClientLoginModule. When these two LoginModules are used as stand-alone modules, they retrieve the user name and credentials from the JAAS NameCallback and PasswordCallback. By setting the module option password-stacking to useFirstPass, they instead retrieve the user name and password credential from the JAAS shared state map, which can be set by other stacked LoginModules. The major difference between ClientLoginModule and AltClientLoginModule is that the latter delays the association of caller identity to the current thread until the JAAS commit phase.
The Content Engine EJBs are secured under a security domain named FileNet®, while application logins are configured by using the FileNetP8Engine JAAS stanza in the JBoss login-config.xml file.
FileNetP8Engine {
com.foo.UserNamePasswordLoginModule optional;
com.foo.ClientSecurityTokenLoginModule optional;
org.jboss.security.ClientLoginModule required
multi-threaded=true password-stacking=useFirstPass
}
FileNet{
org.jboss.security.auth.spi.LdapLoginModule sufficient;
com.foo.ServerSecurityTokenLoginModule sufficient;
}
The following paragraphs describe the WS-EAF implementation of these two LoginModules.
public boolean login() throws LoginException
{
Callback callbacks[] = new Callback[1];
//Callback used to retrieve WSI security header.
callbacks[0] = new TextInputCallback("WSIHEADER");
String wsiHeader = ((TextInputCallback)callbacks[0]).getText();
// Set up the shared state.
sharedState.put("javax.security.auth.login.name","anything");
//The stacked ClientLoginModule will retrieve this as a credential.
sharedState.put("javax.security.auth.login.password", wsiHeader);
return true;
}
Because we are passing the entire WS-Security header to JBossSX, we use only the password key and put arbitrary data as the value of the name key. It is also safe to not set anything into the name in this case. You can also set the name key to a username and the password key to a password in cases where your binary security token can be decomposed to retrieve user name and password values.
public class FnDemoServerBinaryTokenLoginModule extends
AbstractServerLoginModule {
private Principal identity;
public boolean login() throws LoginException {
super.loginOk = false;
Callback callbacks[] = new Callback[1];
//Callback used to retrieve wsi security header.
callbacks[0] = new PasswordCallback("pass", false);
super.callbackHandler.handle(callbacks);
String wsiHeader =
new String(((PasswordCallback)callbacks[0]).getPassword());
Parse the wsi security header and extract credentials.
Do actual authentication here and find out the username.
//Authentication passed. Set up JBoss identity.
if (identity == null) {
identity = super.createIdentity(username);
}
super.loginOk = true;
return super.loginOk;
}
protected Principal getIdentity() {
return identity;
}
protected Group[] getRoleSets() throws LoginException {
//Set up the caller principal as current user.
Group[] groups = {new SimpleGroup("CallerPrincipal")};
groups[0].addMember(identity);
return groups;
}
Inside the login method, the WS-Security header is retrieved by using PasswordCallback. The getIdentity and getRolesSets methods are template methods of AbstractServerLoginModule. They must be implemented to provide the caller's primary identity and group information. Because the Content Engine does not use role-based Java EE security, we set up the CallerPrincipal group, which provides the application identity rather than the security domain identity.
For more information about the JBoss security framework and the JBossSX SPI, see Resources.