I would like to describe a JAAS login configuration for a rich client. In this case, I am using the JBoss6 and the MySQL database to store user, password and role information. Firstly, I specified my own security domain on the server for my application. I added my security domain using name “my-domain” into login-config.xml. You can find this file with a default profile in a directory:
<jboss_home_dir>/server/default/conf/login-config.xml
My security domain:
<application-policy name="my-domain"> <authentication> <login-module code="org.jboss.security.ClientLoginModule" flag="required"> <module-option name="restore-login-identity">true</module-option> </login-module> <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule" flag = "required"> <module-option name = "password-stacking">useFirstPass</module-option> <module-option name = "dsJndiName">java:/AuthDS</module-option> <module-option name = "principalsQuery">SELECT password FROM COM_USER WHERE login=?</module-option> <module-option name = "rolesQuery">SELECT role.id, 'Roles' FROM COM_ROLE role, COM_USER_ROLE map,COM_USER principal WHERE role.GUID = map.ROLE_ID AND map.USER_ID = principal.GUID AND principal.login=?</module-option> <module-option name="hashAlgorithm">MD5</module-option> <module-option name="hashEncoding">Base64</module-option> <module-option name="hashCharset">UTF-8</module-option> <module-option name ="unauthenticatedIdentity">guest</module-option> </login-module> </authentication> </application-policy>
This domain has two login modules. The first one is for a client login module (call-back method). The second one, database login module, is responsible for the user, password and roles. For database module, I need a data source (AuthDS), which need to be deployed on the server. For the password field in the user table, I am using the MD5 hash algorithm, Base64 hash encoding and UTF-8 hash charset. I need to hash the password before I save it in the database. To do this, I am using the method:
import org.jboss.crypto.CryptoUtil;
public String hashPassword(String user, String password) {
return CryptoUtil.createPasswordHash("MD5", "Base64", "UTF-8", user, password);
}
For LDAP authentication, you need to change the database login module to the LDAP login module. In my EAR component, I also need to specify the security domain. This can be done directly in the maven configuration:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-ear-plugin</artifactId> <version>2.5</version> <configuration> <jboss> <version>5</version> <security-domain>my-domain</security-domain> </jboss> <version>6</version> <defaultLibBundleDir>lib</defaultLibBundleDir> <unpackTypes>war,ejb</unpackTypes> </configuration> </plugin>
Or you can create jboss-app.xml manually and put this file in the EAR package in META-INF directory.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-app PUBLIC "-//JBoss//DTD Java EE Application 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-app_5_0.dtd"> <jboss-app> <security-domain>my-domain</security-domain> </jboss-app>
You can also specify the security domain on the EJB service with the annotation.
import org.jboss.annotation.security.SecurityDomain;
@Stateless(name="MyService")
@Remote(MyServiceBI.class)
@SecurityDomain("my-domain")
public class MyServiceBean implements ServiceBI {
....
}
Here is an useful comment from Octavio:
The annotation org.jboss.annotation.security.SecurityDomain that you mention worked fine in JBoss 4. In JBoss 6 the annotation was moved to a different package: org.jboss.security.annotation. However, JBoss 6.0 and 6.1 ignore this annotation completely, so the EJB gets deployed without a security domain. This is probably why I was not getting any security events in my log. The application needs to be secured with a jboss-app.xml.
Now you can create a simple client application with dependencies to the ejb-client of my EJB project and JBoss client libraries. I created the method for login like this:
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginException;
import org.jboss.security.auth.callback.AppCallbackHandler;
import org.jboss.security.client.SecurityClient;
import org.jboss.security.client.SecurityClientFactory;
public void login(String user, String password) {
try {
// Callback hnadler
AppCallbackHandler apch = new AppCallbackHandler(user, password.toCharArray());
// Setup the login configuration
Configuration.setConfiguration(new LoginAuthConfiguration());
// Create security client and login to the server
SecurityClient ssclient = SecurityClientFactory.getSecurityClient();
ssclient.setJAAS("client-module", apch);
ssclient.setVmwideAssociation(true);
ssclient.login();
} catch (LoginException ex) {
LOGGER.error("Error login: " + ex.getMessage(), ex);
} catch (Exception e) {
LOGGER.error("Error creating the secyrity client.", e);
}
}
I am using my own implementation of the login configuration LoginAuthConfiguration. This configuration always returns the same client login module with flag REQUIRED. The client is using the org.jboss.security.ClientLoginModule. You can specify more configurations depended on the name.
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
/**
* The login client configuration.
* @author Andrej Petras <andrej@ajka-andrej.com>
*/
public class LoginAuthConfiguration extends Configuration {
/** The jBoss client login module. */
private static final String JBOSS_CLIENT_MODULE = "org.jboss.security.ClientLoginModule";
/**
* {@inheritDoc}
*/
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry conf = new AppConfigurationEntry(JBOSS_CLIENT_MODULE, REQUIRED, new HashMap<String, Object>());
return new AppConfigurationEntry[]{conf};
}
/**
* {@inheritDoc}
*/
@Override
public void refresh() {
}
}
To create the initial context I am using a no clustering JNDI configuration:
java.naming.provider.url.port=8443 java.naming.provider.url.protocol=https java.naming.provider.url=jnp://localhost:1099 j2ee.clientName=my-client java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming.client
Now you can call EJB methods. Firstly, you need to call the method “login”, which provides the login mechanisms to the server. After successful executing of the login method you can call your services. In the jboss-logging.xml file I enabled the log configuration of login modules.
<logger category="org.jboss.security.ClientLoginModule">
<level name="TRACE"/>
</logger>
<logger category="org.jboss.security.auth.spi.DatabaseServerLoginModule">
<level name="TRACE"/>
</logger>
<logger category="org.jboss.security">
<level name="TRACE"/>
</logger>
Finally, you can see logs in the server log file when you log in with the client.
TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.my-domain] Begin isValid, principal:user, cache info: null TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.my-domain] defaultLogin, principal=user TRACE [org.jboss.security.auth.login.XMLLoginConfigImpl] Begin getAppConfigurationEntry(my-domain), size=12 TRACE [org.jboss.security.auth.login.XMLLoginConfigImpl] End getAppConfigurationEntry(my-domain), authInfo=AppConfigurationEntry[]: [0] LoginModule Class: org.jboss.security.ClientLoginModule ControlFlag: LoginModuleControlFlag: required Options: name=restore-login-identity, value=true [1] LoginModule Class: org.jboss.security.auth.spi.DatabaseServerLoginModule ControlFlag: LoginModuleControlFlag: required Options: name=hashCharset, value=UTF-8 name=hashAlgorithm, value=MD5 name=principalsQuery, value=SELECT password FROM COM_USER WHERE login=? name=unauthenticatedIdentity, value=guest name=hashEncoding, value=Base64 name=dsJndiName, value=java:/AuthDS name=rolesQuery, value=SELECT role.id, 'Roles' FROM COM_ROLE role,COM_USER_ROLE map,COM_USER principal WHERE role.GUID = map.ROLE_ID AND map.USER_ID = principal.GUID AND principal.login=? name=password-stacking, value=useFirstPass TRACE [org.jboss.security.ClientLoginModule] Security domain: tc-system TRACE [org.jboss.security.ClientLoginModule] Enabling restore-login-identity mode TRACE [org.jboss.security.ClientLoginModule] Begin login TRACE [org.jboss.security.ClientLoginModule] Obtained login: user, credential.class: [C TRACE [org.jboss.security.ClientLoginModule] End login TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] initialize TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Security domain: my-domain TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Saw unauthenticatedIdentity=guest TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Password hashing activated: algorithm = MD5, encoding = Base64, charset = UTF-8, callback = null, storeCallback = null TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] DatabaseServerLoginModule, dsJndiName=java:/AuthDS TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] principalsQuery=SELECT password FROM COM_USER WHERE login=? TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] rolesQuery=SELECT role.id, 'Roles' FROM COM_ROLE role,COM_USER_ROLE map,COM_USER principal WHERE role.GUID = map.ROLE_ID AND map.USER_ID = principal.GUID AND principal.login=? TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] suspendResume=true TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] login TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] suspendAnyTransaction TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Excuting query: SELECT password FROM COM_USER WHERE login=?, with username: user TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Obtained user password TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] resumeAnyTransaction TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] User 'user' authenticated, loginOk=true TRACE [org.jboss.security.ClientLoginModule] commit, subject=Subject: TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] commit, loginOk=true TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] getRoleSets using rolesQuery: SELECT role.id, 'Roles' FROM COM_ROLE role,COM_USER_ROLE map,COM_USER principal WHERE role.GUID = map.ROLE_ID AND map.USER_ID = principal.GUID AND principal.login=?, username: user TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] suspendAnyTransaction TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Excuting query: SELECT role.id, 'Roles' FROM COM_ROLE role,COM_USER_ROLE map,COM_USER principal WHERE role.GUID = map.ROLE_ID AND map.USER_ID = principal.GUID AND principal.login=?, with username: user TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Assign user to role Authenticated TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] Assign user to role u-tc-admin TRACE [org.jboss.security.auth.spi.DatabaseServerLoginModule] resumeAnyTransaction TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.my-domain] defaultLogin, lc=javax.security.auth.login.LoginContext@2b8515bf, subject=Subject(450299456).principals=org.jboss.security.SimplePrincipal@1216919532(user)org.jboss.security.SimpleGroup@450584964(Roles(members:Authenticated,u-tc-admin)) TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.my-domain] updateCache, inputSubject=Subject(450299456).principals=org.jboss.security.SimplePrincipal@1216919532(user)org.jboss.security.SimpleGroup@450584964(Roles(members:Authenticated,u-tc-admin)), cacheSubject=Subject(954150626).principals=org.jboss.security.SimplePrincipal@1216919532(user)org.jboss.security.SimpleGroup@450584964(Roles(members:Authenticated,u-tc-admin)) TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.my-domain] Inserted cache info: org.jboss.security.plugins.auth.JaasSecurityManagerBase$DomainInfo@447f1499[Subject(954150626).principals=org.jboss.security.SimplePrincipal@1216919532(user)org.jboss.security.SimpleGroup@450584964(Roles(members:Authenticated,u-tc-admin)),credential.class=[C@1907659718,expirationTime=1306062790734] TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.my-domain] End isValid, true
Useful links:
JAAS & JBoss: Client Authentication
6 Responses for "JBoss 6: Client authentication, security domain"
Andrej,
Thanks for your post. I hope you don’t mind a couple of questions:
When you build the security client, you are passing “client-module” as a parameter to ssclient.setJAAS. Can you clarify where that string comes from?
I’m not getting security events in my log. Did you make any other changes to jboss-logging.xml apart from adding the three loggers?
Octavio
Andrej,
My 10+ hours of trouble-shooting (and frustration) have led me to the following conclusion:
The annotation org.jboss.annotation.security.SecurityDomain that you mention worked fine in JBoss 4. In JBoss 6 the annotation was moved to a different package: org.jboss.security.annotation. However, JBoss 6.0 and 6.1 ignore this annotation completely, so the EJB gets deployed without a security domain. This is probably why I was not getting any security events in my log.
The application needs to be secured with a jboss-app.xml file as you mention in your post.
Thanks once again for your guidance,
Octavio
Hi Octavio,
thank you for your comments. I will add these to my post.
Andrej
Hi Octavio,
if you want to have more than one login confiuration you can implement this in the LoginAuthConfiguration.getAppConfigurationEntry(String name).
The input parameter in this method is the same parameter as you are passing in the ssclient.setJAAS.
In my post I have only one login configuration so it doesn’t matter what name of the parameter is.
Andrej
[...] You should read the post JBoss 6: Client authentication, security domain by Andrej. About Accessing a Database Connection Pool from a Session EJB in [...]
Andrej,
With respect to Octavio’s comment regarding the SecurityDomain annotation. It did in fact move for 6.x. However, it moved to org.jboss.ejb3.annotation. If you use the SecurityDomain annotation from that package, it works fine.
Karl
Leave a reply