Custom Glassfish Security Realm does not work (unable to find LoginModule)

Wolkenarchitekt picture Wolkenarchitekt · Dec 24, 2010 · Viewed 10.3k times · Source

I'm trying to get a Custom Security Realm in Glassfish working (i tried 3.0.1 final and 3.1 B33). I read nearly all tutorials about this, but it doesn't not work on my System. I'm getting the error

Login failed: javax.security.auth.login.LoginException: unable to find LoginModule class: com.company.security.realm.CustomLoginModule

when trying to login.

Here is what i did: I created a little Maven project, which contains the needed Realm class, CustomRealm, and the corresponding LoginModule, CustomLoginModule. My pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany</groupId>
  <artifactId>security.realm</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Custom JDBCRealm</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
        <groupId>org.glassfish.security</groupId>
        <artifactId>security</artifactId>
        <version>3.1-b33</version>
    </dependency>
  </dependencies>

  <build>
      <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
                <optimise>true</optimise>
                <debug>true</debug>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>    
      </plugins>
  </build>
</project>

My Custom Realm class:

package com.company.security.realm;

import com.sun.appserv.security.AppservRealm;
import com.sun.enterprise.security.auth.realm.BadRealmException;
import com.sun.enterprise.security.auth.realm.InvalidOperationException;
import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
import com.sun.enterprise.security.auth.realm.NoSuchUserException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

public class CustomRealm extends AppservRealm
{
    Vector<String> groups = new Vector<String>();

    private String jaasCtxName;

    private String startWith;

    @Override
    public void init(Properties properties)
    throws BadRealmException, NoSuchRealmException {
        jaasCtxName = properties.getProperty("jaas-context", "customRealm");
        startWith = properties.getProperty("startWith", "z");
        groups.add("dummy");
    }

    @Override
    public String getAuthType()
    {
        return "Custom Realm";
    }

    public String[] authenticate(String username, char[] password) 
    {
        // if (isValidLogin(username, password))
        return (String[]) groups.toArray();
    }

    @Override
    public Enumeration getGroupNames(String username) 
    throws InvalidOperationException, NoSuchUserException 
    {
        return groups.elements();
    }

    @Override 
    public String getJAASContext()
    {
        return jaasCtxName;
    }

    public String getStartWith()
    {
        return startWith;
    }
}

My LoginModule class:

package com.company.security.realm;

import com.sun.appserv.security.AppservPasswordLoginModule;
import com.sun.enterprise.security.auth.login.common.LoginException;
import java.util.Set;
import org.glassfish.security.common.PrincipalImpl;

public class CustomLoginModule extends AppservPasswordLoginModule
{
    @Override
    protected void authenticateUser() throws LoginException
    {
        _logger.info("CustomRealm : authenticateUser for " +  _username);
        final CustomRealm realm = (CustomRealm)_currentRealm;

        if ( (_username == null) || (_username.length() == 0) || !_username.startsWith(realm.getStartWith())) 
            throw new LoginException("Invalid credentials");

        String[] grpList = realm.authenticate(_username, getPasswordChar()); 
        if (grpList == null) { 
            throw new LoginException("User not in groups");
        }

        _logger.info("CustomRealm : authenticateUser for " +  _username);

        Set principals = _subject.getPrincipals();
        principals.add(new PrincipalImpl(_username));

        this.commitUserAuthentication(grpList);

    }

}

I compiled this Maven project and copyied the resulting JAR-file to the Glassfish/lib directory. Then i added the Security Realm "customRealm" to my Glassfish with asadmin:

asadmin create-auth-realm 
  --classname com.company.security.realm.CustomRealm 
  --property jaas-context=customRealm:startWith=a customRealm

I also referenced the LoginModule class for the JAAS context of my Custom Realm, therefore i inserted this into the login.conf of my domain:

customRealm {
  com.company.security.realm.CustomLoginModule required;
};

Although this LoginModule SHOULD BE on the Glassfish classpath, as it's classfile is packaged in the JAR that i put into the Glassfish/lib-dir, it cannot be found when i try to login. For login, i build a simple JSF-project, which calls the HttpServletRequest-login-method of Servlet 3.0. When trying to login i'm getting the following Exception:

2010-12-24T14:41:31.613+0100|WARNING|glassfish3.0.1|
javax.enterprise.system.container.web.com.sun.web.security|_ThreadID=25;
_ThreadName=Thread-1;|Web login failed: Login failed: 
javax.security.auth.login.LoginException: unable to find LoginModule class:
com.company.security.realm.CustomLoginModule

Anybody got an idea what i can do that Glassfish loads the LoginModule-class?

Answer

Wolkenarchitekt picture Wolkenarchitekt · Dec 27, 2010

Got it. Seems like newer Glassfish versions require that the Security Realm and the LoginModule are packaged as an OSGi module, which should then be copied into glassfish/modules.

Therefore i changed my pom.xml to create an OSGi bundle, which contains both the CustomRealm and the CustomLoginModule.

Here it is:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mycompany</groupId>
    <artifactId>security.realm</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>bundle</packaging>

    <name>Custom JDBCRealm OSGi</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.glassfish.security</groupId>
            <artifactId>security</artifactId>
            <version>3.1-b33</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <optimise>true</optimise>
                    <debug>true</debug>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Export-Package>
                            ${project.groupId}.${project.artifactId};version=${project.version}
                        </Export-Package>
                        <Import-Package>
                            com.sun.appserv.security,
                            org.glassfish.security.common,
                            com.sun.enterprise.security.auth.realm,
                            com.sun.enterprise.security.auth.login.common,
                            java.util,
                            javax.security.auth
                        </Import-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

EDIT

Found a good additional resource here: http://blogs.oracle.com/nithya/entry/modularized_osgi_custom_realms_in , where the Realm and it's LoginModule is build as a hk2-jar.