Glassfish provides support for Custom Realms and Custom Login Modules that are based on the JAAS framework. This post explains how to write a simple Realm class and its corresponding LoginModule, configure them with an illustration of a simple web application that uses this realm.
Custom Realm
The Custom Realm should extend com.sun.appserv.security.AppservRealm. The Realm class is basically meant to provide user and group-related information. The methods to be implemented are
i) public void init(Properties properties )throws BadRealmException, NoSuchRealmException
- This method is invoked during server startup when the realm
is initially loaded.
The realm can do any initialization it needs in this method. The Properties is a set of key-value pairs configured while creating the Realm and are present in domain.xml. Among the other custom properties, there is a property jaas-context (which is explained later in this post). This property should be set using the call setProperty method implemented in the parent class. If the
method returns without throwing an exception, the Enterprise Server assumes
that the realm is ready to service authentication requests. If an
exception is thrown, the realm is disabled.
ii) public String getAuthType() - This method returns a descriptive string representing the type
of authentication done by this realm.
iii) public Enumeration getGroupNames(String user) throws InvalidOperationException, NoSuchUserException - This method returns the group names the user belongs to as an Enumeration of Strings.
Custom LoginModule
The Custom LoginModule should extend com.sun.appserv.security.AppservPasswordLoginModule. This class should override the method
abstract protected void authenticateUser() throws LoginException
This method performs the actual custom authentication, by either using a database, or LDAP or a file or even a simple Hashtable as illustrated in the attached sample code. The custom login
module must not implement any of the other methods, such as login(), logout(), abort(), commit(), or initialize(). Default implementations
are provided in AppservPasswordLoginModule which
hook into the Enterprise Server infrastructure.
The custom login module can access the following protected object
fields, which it inherits from AppservPasswordLoginModule.
These contain the user name, password of the user to be authenticated and the currentRealm class.
protected String _username;
protected String _password;
protected com.sun.enterprise.security.auth.realm.Realm _currentRealm;
The authenticateUser() method should end with a call to the commitUserAuthentication(String[] authenticatedGroupList) method where the authenticatedGroupList is the list of groups the user belongs to.
As can be observed, the realm class is isolated from the LoginModule. The Realm is capable of capturing arbitrary configuration information and can help in obtaining the Group information. The Group information from the Realm can be populated into the authenticated JAAS subject during commit() phase following a successful LoginContext.login() call on the authentication module. This populated group information is then used by the container in its authorization policy decisions.
Attached here is the source code of a simple sample realm class and the custom module. In this example, the Realm class stores the user-group information in a hashtable. The LoginModule class stores the user-password information in a hashtable and performs authentication. It obtains the authenticatedGroupList from the Realm class' getGroups(username) method.
To test this sample(it works with both GF v2 and v3), download and install Glassfish v3 from here, drop the binaries of this realm and custom module in <GF-ROOT>/domains/domain1/lib/, start the server and create the realm using the Admin console. The realm classname should be specified as com.samplerealm.SampleRealm.
An additional realm property jaas-context should be specified to say sampleRealm. This value should refer to the SampleLoginModule class in the
<GF-ROOT>/domains/domain1/config/login.conf
file as follows:
sampleRealm {
com.samplerealm.SampleLoginModule required;
};
where sampleRealm refers to the value defined in the jaas-context property.
As can be seen from the source files, the users configured in this realm are userA, userB whose corresponding passwords are abc123, xyz123. userA has been configured in the group devGroup, while userB belongs to testGroup. To test this realm, this web-application can be used.
Observe that the web.xml of the web-app contains the following :
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>SampleRealm</realm-name>
</login-config>
where SampleRealm in the <login-conf><realm-name> element points to the name of the configured Realm as can be seen in the server's domain.xml
<auth-realm classname="com.samplerealm.SampleRealm" name="SampleRealm">
<property name="jaas-context" value="sampleRealm"></property>
</auth-realm>
To access the web-app using the group names of the user, the following mapping between the role and group is required in sun-web.xml:
<security-role-mapping>
<role-name>tester</role-name>
<group-name>devgroup</group-name>
</security-role-mapping>
where the role-name matches the configured role in the auth-constraint of web.xml
<auth-constraint>
<description/>
<role-name>tester</role-name>
</auth-constraint>
and group-name is the corresponding group the user belongs to as defined in the custom realm class. So if the application is accessed using the user/password: userA/abc123, the user is authorized to view the pages, since he belongs to the devGroup, but not userB/xyz123 (who belongs to testGroup).