Thursday, January 15, 2015

How to setup a custom JaaS Login Module in Glassfish 4.1

A very basic, minimal requirements glassfish4 and JAAS login module can be gotten from :

git clone https://github.com/byorn/glassfish4-jaas

Once cloned open the project with Netbeans 8.1

Please find below the steps to create a basic custom login module in Glassfish 4.1

Step 1:  Create the login page:  login.jsp

    <form method=post action="j_security_check" >
    <span>Username:</span>
    
      <input type="text"  name= "j_username" >
       <span>Password:</span>
     
      <input type="password"  name= "j_password" >
      <input type="submit" value="Login" class="btn"/>
    
  </form>


Step 2:  Configure Web.xml 

<security-constraint>
        <web-resource-collection>
            <web-resource-name>secure</web-resource-name>
            <url-pattern>/secure/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>
    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <login-config>
        <auth-method>FORM</auth-method>
         <realm-name>myCustomRealm</realm-name>
        <form-login-config>
            <form-login-page>/login.jsp</form-login-page>
            <form-error-page>/error.jsp</form-error-page>
        </form-login-config>
    </login-config>


If you checked out the project source you can see a folder called /secure/index.jsp.
This page can be accessed only if the logged in user has the role 'admin'
Notice that, the welcome page:
 <welcome-file-list>
        <welcome-file>secure/index.jsp</welcome-file>
    </welcome-file-list>
is pointing to this secure page. Once the user tries to call this page from the browser, the system
will prompt the login.jsp page, asking the user to login first.

Notice: the myCustomRealm. This will be mentioned in the next few steps. Basically, later on we create a custom Realm called myCustomRealm, which actually points to JAR file..(The login module) that you are about to create.

Step 3: configure Sun-web.xml or glassfish-web.xml

These entries need to be present in your sun-web.xml. It could be glassfish-web.xml also, depending on how you created your web module in Netbeans.

  <security-role-mapping>
    <role-name>admin</role-name>
    <group-name>admin</group-name>
  </security-role-mapping>

Step 4: Build the Login Module 

You need to create two files.
 1) A LoginModule that extends com.sun.appserv.security.AppservPasswordLoginModule
 2) A Realm  which extends com.sun.appserv.security.AppservRealm;

In order to get these files, you need to import the following JARS to your project
* glassfish-ee-api.jar
*  security.jar
These Jars can be found in :   glassfish-4.1\glassfish\modules

1)  LoginModule

public class MyLoginModule extends AppservPasswordLoginModule{

    public MyLoginModule() {

      System.out.println("MyRealm LoginModule - Construction");
   }
 
    @Override
    protected void authenticateUser() throws LoginException {
        System.out.println("Going to Log In ............................");
        String userString = _username;
        
        //HERE YOU CAN GET A HANDLE TO A JDBC CONNECTION POOL IN GLASSFISH
        //FROM THE JNDI NAME, AND EXECUTE A SQL TO RETRIEVE ALL THE GROUPS 
        //THE USER BELONGS TO

        String[] groups = {"admin"};

        commitUserAuthentication(groups);
        

    }


}

In he authenticateUser() method you can get a handle to a JDBC Resource connection pool
and retrieve all the groups the user belows to.
Notice from _username and _password, you can get access to the username and password that was passed from the login page.

2) The Realm Class
public class MyRealm extends AppservRealm{
private static final String JAAS_CONTEXT="jaas-context";
   

@Override
   public void init(Properties properties) throws BadRealmException, NoSuchRealmException {

       System.out.println("Init MyRealm");

       // Pass the properties declared in the console to the system
       String propJaasContext=properties.getProperty(JAAS_CONTEXT);
       if (propJaasContext!=null) {
          setProperty(JAAS_CONTEXT, propJaasContext);
       }
   }
 
    @Override
    public String getAuthType() {
       return "WebAuthorization";
    }

    @Override
    public Enumeration getGroupNames(String string) throws InvalidOperationException, NoSuchUserException {
      // return Collections.enumeration(getGroups());
        return null;
    }

}

Above is the minimal configuration needed.


Step 5 Deploy the Login Module. 

The login module in \glassfish4-jaas\myjaasloginmodule\dis\myjaasloginmodule.jar  should be copied to  \[glassfish installation] \glassfish\domains\domain1\lib folder

Note: My glassfish in netbeans is configured to run in domain1. Yours may be different.
In netbeans under services tab -> servers you can delete the galssfish server and add it again.
Here you get to specify your custom domain. If the domain you specify is 'custom_domain' then you need to put the jar file to \custom_domain\lib folder.

Step 6  Edit  login.conf
Open login.conf residing in \glassfishinstallation\glassfish\domains\domain1\config

Add the below entry to the end of the file

myloginmoduleRealm {
myloginmodule.MyLoginModule required;
};


Step 7  Configure the security realm in Glassfish Admin portal

Open http://localhost:4848/



In above console, notice domain1,
notice that the security realm myCustomRealm is created under server-config and not under default-config.




Add property called jaas-context and provide the value to be myloginmoduleRealm which is actually the name mentioned in the login.conf file.

This is pretty much all you need to do. Let me know if you ran into any difficulties.

11 comments:

  1. Thanks for the tutorial! However, when I try to follow get always a login failure and the following message in the server logs: "WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: Invalid null input: name" Any idea what could cause the problem?

    ReplyDelete
  2. have you check the login form (step 1) the username and password fields should be j_username, and j_password?

    ReplyDelete
    Replies
    1. Thanks for the nice article.
      I am able to do authentication successfully but not able to get j_username value in error.jsp file to display unsuccessful login attempts for a user and finally lockout the user.

      Delete
  3. Thanks for your reply! Actually I managed to solve the problem by beginning once more from scratch. But I don't know what difference made the thing now work. But well, it's working now... :-)

    ReplyDelete
  4. Thanks a lot.
    It's hard to find which jar contain the AppservRealm and the login module.

    ReplyDelete
  5. In above console, notice domain1,
    notice that the security realm myCustomRealm is created under server-config and not under default-config.
    is the myCustomRealm created automatically???

    ReplyDelete
    Replies
    1. No you have to create it by your self. myCustomRealm can be any name.

      Delete
  6. Hi Byron

    do you have an example of how to get the JDBC connection?
    your code example does not show how....

    //HERE YOU CAN GET A HANDLE TO A JDBC CONNECTION POOL IN GLASSFISH
    //FROM THE JNDI NAME, AND EXECUTE A SQL TO RETRIEVE ALL THE GROUPS
    //THE USER BELONGS TO

    THanks, Tim

    ReplyDelete
    Replies
    1. I want to get j_username value in error.jsp file

      Delete
  7. I am able to do authentication successfully but not able to get j_username value in error.jsp file to display unsuccessful login attempts for a user and finally lockout the user

    ReplyDelete