Computing @ 40's

Trying to overcome the obsolescence

How to create a custom realm in Glassfish 3.1.2.2

Nowadays, if you plan to have a Web site with user registration availability, your first concern must be to secure your application using the most standard ways to do it. This ensures that you have everything tied and you will not leave any gap to any possible attack.

At work, we had the need, last year, to secure a Web application that I was creating from the ground. I started with no knowledge at the beggining, but thanks to a little course of Java EE and my research about custom realms finally I found the right and standard way that I introduce you here.

This post will talk about securing a Web application using a custom realm in Glassfish. I will explain the step by step process of creating a custom realm and how to apply it in a Web application starting from the ground.

So the objectives in this article will be:

  1. Create a custom realm in Glassfish
  2. Configure a Glassfish Server to use this custom Realm
  3. Apply this custom realm in a little “Login and Hello World” example.

As always, the configuration used to create this little project can be found here.

Anyway, if you find this article not good enough, I show you some useful links where I could find some of the pieces of information that finally lead me to the solution I give here:

  1. This blog post from Nithya Subramanian is a good starting point and give you almost all the clues: https://blogs.oracle.com/nithya/entry/groups_in_custom_realms
  2. Here is another good example, maybe a bit confusing with a little tutorial very similar to the one you are reading: https://blogs.oracle.com/phendley/entry/creating_and_using_a_glassfish
  3. There is also some useful information at Java EE 6 tutorial: http://docs.oracle.com/javaee/6/tutorial/doc/bnbxj.html

1.- Introducing Java EE Realms.

First of all I would like to make a definition, in my own words, of what in Glassfish is called a security realm. A security realm is a security context where an application exists. This context define a set of user / groups of users and the authenthication methods used to access it. Imagine that you have a Web site that need some kind of security control, that is, that need users to be identified with a username / password. You can check that the user / password exists in some table in your database, or maybe you you want to validate the correctness of that credentials (username / password) using an LDAP server. Every one of that situations require you to define a security realm to control access to your web pages.

Glassfish comes with some predefined realms that can be useful at the most of occasions. I will make some comments about three of these realms:

  1. File Realm: information about users are stored in a file inside the server. The problem with this approach, in my opinion, is that I didn’t found how to interact with the user file, and this makes it difficult to have the users managed dinamically. For example, it makes more complicated to have a user auto-registration procedure. Additionally, many times is useful to have information related to the user, not only the username / password, and this cannot be achieved with a user file only. The class associated with this realm is com.sun.enterprise.security.auth.realm.file.FileRealm.
  2. JDBCRealm: this one is a more powerful realm. It makes it possible to validate a username/password against a table in a database. Generally this could be enough, but it is still a bit too rigid. For example, imagine that you want users to be able to validate using a username or their e-mail. With JDBCRealm approach there is only one key to validate user. The class associated with this realm is com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm
  3. LDAPRealm: this one can be used if you want to validate a username / password againts a LDAP server. This can be useful in enterprise environments that have LDAP servers for network validation. The class associated with this realm is com.sun.enterprise.security.auth.realm.ldap.LDAPRealm

With these three realms you can have your needs covered. But sometimes you need something else that can’t be satisfied with them. For example: in our enterprise we had users that needed LDAP validation but some others that doesn’t have network user must have a validation against a user table stored in a database. I dindn’t found a way to use more than one realm inside an application (as far as I know you can only assign one Realm to your web.xml configuration file). In cases like that you will need a Realm defined by yourself, that is, a custom Realm.

To create a custom realm in Glassfish is relativelly easy. But you need to follow some steps to do it. I will show you here how to do this. The steps to do it are:

  1. Create the Java projects that you will use to implement the logic of username / password validation and will use that implementation.
  2. Configure Glassfish configuration files login.conf and domain.xml to make Glassfish aware of your new custom realm.
  3. Associate your custom realm with your Web project

After that you will have a custom solution for validating users with your own procedure.

2.- Creating the Java projects needed.

We will need at least two Java projects to make this example:

  1. The Dynamic Web Java project that we want to secure.
  2. An utility java project where we will implement our realm. This is a simple Java project where we will put the classes related to the security.

Creating the Dynamic Web Java Project

We can start with the base project that I created in my previous article, Creating a simple JSF Web application from the ground. You can read that article and follow the steps or directly download the resulting project at this link. Remember that this project has been setup with Eclipse to work with Glassfish. It is configured also to use RichFaces.

After creating this project we will add a Login page and a main page, and, finally, the initial page index.xhtml will redirect  automatically to the Login page. Here are the files:

  • index.xhtml: located at WebContent root. This file is referenced at web.xml as a welcome file, but it is used only to redirect the user to the login page (/auth/login.xhtml). Notice that we are referencing the base context path using this Javaserver Faces constant:#{facesContext.externalContext.requestContextPath}
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >
    <html xmlns:h="http://java.sun.com/jsf/html">
    
    <h:head>
        <meta http-equiv="Refresh" content="0;url=#{facesContext.externalContext.requestContextPath}/auth/login.xhtml"></meta>
    </h:head>
    
    </html>
    
  • /auth/login.xhtml: this page is the login page. Our backing bean here is called loginForm, located at package com.booreg.auth
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    >
    
    <h:head/>
    <h:body>
        <h:form>
            <h:panelGrid columns = "2">
                <h:outputText value="Username or e-mail:" />
                <h:inputText  value="#{loginForm.usid}" />
                <h:outputText value="Password:" />
                <h:inputSecret value="#{loginForm.pass}" />
            </h:panelGrid>
    
            <h:commandButton value="Login" action="#{loginForm.doLogin}" />
        </h:form>
    
        <h:messages />
    
    </h:body>
    </html>
    
    
  • LoginForm.java: the backing bean referenced at loginForm.xhtml. Notice that logged user is called Principal in Java EE terminology. We check if exist a principal, and if not, we do a login to a HttpServletRequest object called request. If everything goes fine the outcome will be a “word” called main. This outcome is configured in faces-config.xml to redirect the user to page main.xhtml. Internally the method login of  HttpServletRequest will end up to our custom login module classes that I will explain later.
    package com.booreg.auth;
    
    import java.io.Serializable;
    import java.security.Principal;
    
    import javax.faces.application.FacesMessage;
    import javax.faces.bean.ManagedBean;
    import javax.faces.bean.ViewScoped;
    import javax.faces.context.FacesContext;
    import javax.servlet.http.HttpServletRequest;
    
    /**
    * Backing bean class for loginForm.xhtml page
    */
    
    @ManagedBean
    @ViewScoped
    public class LoginForm implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        private String usid;
        private String pass;
    
        // Getters and setters
    
        public String getUsid()          { return usid;    }
        public String getPass()          { return pass;    }
    
        public void setUsid(String usid) { this.usid = usid; }
        public void setPass(String pass) { this.pass = pass; }
    
        //**************************************************
        // Public section
        //**************************************************
    
        /**
        * Do login on system
        */
    
        public Object doLogin()
        {
            String result = null;
    
            FacesContext context = FacesContext.getCurrentInstance();
    
            HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
            try
            {
                Principal principal = request.getUserPrincipal();
    
                if (principal == null || !principal.getName().equals(usid)) request.login(this.usid, this.pass);
    
                result = "main";
            }
            catch (Exception e)
            {
                pass = null;
    
                context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), null));
            }
    
            return result;
        }
    }
    
  • main.xhtml: user will be redirectedto this page if login is succesful, thanks to the redirection configured at file faces-config.xml In this case is a simple congratulations page.
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    
    <h:head/>
    <h:body>
        <h:outputText value="You've got it!!!" />
    </h:body>
    </html>
    
  • The web and JSF configuration pages is like this. In this case its purpose is only to redirect navigation in case of sucessful login.
    <?xml version="1.0" encoding="UTF-8"?>
    <faces-config version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xi="http://www.w3.org/2001/XInclude"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd">
    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>main</from-outcome>
            <to-view-id>/main.xhtml</to-view-id>
        </navigation-case>
    </navigation-rule>
    </faces-config>
    
  • Users.java and Usrgp.java: the two classes representing the data model in our database. These classes are the entities that will allow us to acces the data in our database using JPA. I don’t want to extend talking about JPA here, so, if you don’t know it, take it as it is.
    package com.booreg.auth.model;
    
    import java.io.Serializable;
    import java.util.List;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.NamedQueries;
    import javax.persistence.NamedQuery;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    /**
    * The persistent class for the users database table.
    */
    
    @Entity
    @Table(name="users")
    @NamedQueries
    ({
        @NamedQuery(name="UsersByEmail", query="SELECT X FROM Users X WHERE X.usid = ?1")
    })
    
    public class Users implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @Id private String usid;
            private String emal;
            private String pass;
            private String usnm;
    
        @OneToMany(mappedBy="user")
        private List<Usrgp> usrgps;
    
        /** Getters */
    
        public String getUsid()        { return this.usid;     }
        public String getEmal()        { return this.emal;   }
        public String getPass()        { return this.pass;   }
        public String getUsnm()        { return this.usnm;   }
    
        public List<Usrgp> getUsrgps() { return this.usrgps; }
    
        /** Setters */
    
        public void setUsid(String usid)          { this.usid = usid;     }
        public void setEmal(String emal)          { this.emal = emal;     }
        public void setPass(String pass)          { this.pass = pass;     }
        public void setUsnm(String usnm)          { this.usnm = usnm;     }
        public void setUsrgps(List<Usrgp> usrgps) { this.usrgps = usrgps; }
    }
    
    package com.booreg.auth.model;
    
    import java.io.Serializable;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    
    /**
    * The persistent class for the usrgp database table.
    */
    
    @Entity
    public class Usrgp implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @Id private String usgp;
    
        @ManyToOne @JoinColumn(name="USID")    private Users user;
    
        /** Getters */
    
        public String getUsgp() { return this.usgp;    }
        public Users  getUser() { return this.user; }
    
        /** Setters */
    
        public void setUsgp(String usgp) { this.usgp = usgp; }
        public void setUser(Users  user) { this.user = user; }
    }
    

Summary: until now we have just the furnishing of our aplication. The Login page and the main page, the backing bean controlling the Login page, the classes representing our data model in the database and the configuration files, but we have not talked yet about the core of this example. How to make the authorization validation and how it joins with our code. Next steps will show it.

Creating the utility project with our realm implementation.

Create a new simple Java project on you workspace (select File → New → Other → Project → Java Project). We will call this project com-booreg-security.

Create a folder called lib inside you Java project and copy there the file security.jar located at $glassfish-installation-folder\glassfish\modules. Finally put this library inside your classpath. This is a very important step because we will need to override two classes located on that library. If we don’t do that, our project will not compile. I don’t know if there is a better method, but I couldn’t find a way to include this library on the classpath other than this one. So the project, now appears like this:

Adding security-jarAfter that we will create the interface to our Authorization ejb service class. I’ll create this class at package com.booreg.auth.services

package com.booreg.auth.services;

import java.util.List;

/**
* Interface for user validation services
*/

public interface IUserAuthenticationService
{
    /**
     * Validates the password specified by the user
     */

    public void validatePassword(String usid, String password) throws Exception;

    /**
     * Returns the list of groups whom the user belongs to
     */

    public List<String> getGroups(String usid);

}

Then we will create the core of our authentication service. This is a EJB statless bean implementing the interface IUserAuthenticationService. This class must be on our WAR project and I will put in on com.booreg.auth.services package. This class provides us the method that validates the correctness of user and password. The validation procedure checks if the user exists. If not, checks if user has enter his e-mail to authenticate. Finally, if we locate the user with the usid or his e-mail, we check if the pasword is correct. Important!! Notice tat we are not using here password encryption. This is not a good practice at all, but here is done like this to make the example simpler.

UserAuthenticationService.java:


package com.booreg.auth.services;

import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.booreg.auth.model.Users;

/**
 * Session Bean implementation class UserValidationService
 */

@Stateless
@LocalBean
public class UserAuthenticationService implements IUserAuthenticationService
{
    private @PersistenceContext(unitName = "booreg") EntityManager em;

    @Override
    public void validatePassword(String usid, String password) throws Exception
    {
        Users users = em.find(Users.class, usid);

        if (users == null) users = (Users) em.createNamedQuery("UsersByEmail").setParameter(0, usid).getSingleResult();

        if (users == null || !password.equals(users.getPass())) throw new Exception("Username or password not valid");
    }

    @Override
    public List<String> getGroups(String usid)
    {
        // TODO Auto-generated method stub
        return null;
    }
}

Finally you will need to write 2 classes to implement the Realm. These two classes will be on our com-booreg-security project. Also I will add a SecurityUtil utility class that will make the main task.

MyCustomModule.java: this class calls the authentication procedure (located really at SecurityUtil.java class. I harcoded also that the user will belong to a allUsers group.

package com.booreg.auth;

import javax.security.auth.login.LoginException;

import com.sun.appserv.security.AppservPasswordLoginModule;

/**
 * Custom module
 *
 * @author dgisbert
 */

public class MyCustomModule extends AppservPasswordLoginModule
{
    @Override
    protected void authenticateUser() throws LoginException
    {
        SecurityUtil.authenticateUser(_username, _passwd);

        String[] groups = {"allUsers"};
        commitUserAuthentication(groups);
    }
}

MyCustomRealm.java: this class is the bridge between Glassfish server and our custom module. Later we will configure Glassfish to use this class as our authorization realm. This class only contains log methods to show realm information on our server log. The most important step is to call the method setProperty(PARAM_JAAS_CONTEXT, propJaasContext);

package com.booreg.auth;

import java.util.Collections;
import java.util.Enumeration;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

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;

/**
 * Custom Realm class
 *
 * @author dgisbert
 */

public class MyCustomRealm extends AppservRealm
{
    /** JAAS Context parameter name */ public static final String PARAM_JAAS_CONTEXT = "jaas-context";

    /** Authentication type description */

    public static final String AUTH_TPYE = "Authentication done by checking user at table USERS on database";

    @Override
    public void init(Properties properties) throws BadRealmException, NoSuchRealmException
    {
        String propJaasContext = properties.getProperty(PARAM_JAAS_CONTEXT);

        if (propJaasContext != null) setProperty(PARAM_JAAS_CONTEXT, propJaasContext);

        Logger logger = Logger.getLogger(this.getClass().getName());

        String realmName = this.getClass().getSimpleName();

        logger.log(Level.INFO, realmName + " started. ");
        logger.log(Level.INFO, realmName + ": " + getAuthType());
        logger.log(Level.INFO, realmName + " authentication uses jar file com-booreg-security.jar located at $domain/lib folder ");

        for (Entry<Object, Object> property:properties.entrySet()) logger.log(Level.INFO, property.getKey() + ": " + property.getValue());
    }

    @Override
    public String getAuthType()
    {
        return AUTH_TPYE;
    }

    @Override
    public Enumeration<?> getGroupNames(String usid) throws InvalidOperationException, NoSuchUserException
    {
        return Collections.enumeration(SecurityUtil.getGroups(usid));
    }
 }

SecurityUtil.java: this class does the work of looking up the authorizations service on the local service and makes the call to authorizations validation procedure.

package com.booreg.auth;

import java.util.List;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.InitialContext;
import javax.security.auth.login.LoginException;
import com.booreg.auth.services.IUserAuthenticationService;

/**
 * Utility class that implements a lookup method to locate the authorization service class that allows to authenticate users.
 *
 * @author dgisbert
 */

public class SecurityUtil
{
    private static Logger logger = Logger.getLogger(SecurityUtil.class.getName());

    public static final String AUTHENTICATION_INTERFACE = "java:global/com-booreg-war/UserAuthenticationService";

    /**
    * Returns the interface IUserValidationService to the class that will validate username and password.
    */

    private static IUserAuthenticationService lookupUserValidationService(String jndiClassname) throws Exception
    {
        Properties properties = new Properties();

        properties.put("java.naming.factory.initial" , "com.sun.enterprise.naming.SerialInitContextFactory");
        properties.put("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
        properties.put("java.naming.factory.state"   , "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");

        InitialContext remoteContext = new InitialContext(properties);

        Object object = remoteContext.lookup(jndiClassname);

        return (IUserAuthenticationService) object;
    }

    /**
     * Calls the validation service Valida l'usuari / contrassenya donat utilitzant el servei d'utenticació especificat en la classe. Aquest servei es troba en el servidor / port donat
     */

    public static void authenticateUser(String username, char[] password) throws LoginException
    {
        logger.log(Level.INFO, AUTHENTICATION_INTERFACE + " trying to authenticate user " + username);

        try
        {
            IUserAuthenticationService validationService = lookupUserValidationService(AUTHENTICATION_INTERFACE);
            validationService.validatePassword(username, new String(password));
        }
        catch (LoginException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            //TODO El missatge d'error hauria de dependre de l'idioma de l'usuari
            throw new LoginException((new StringBuilder()).append("Error en la validación del usuario: ").append(username).toString() + ". " + e.getMessage());
        }
    }

    /**
     * Returns the groups of this user
     */

    public static List<String> getGroups(String usid)
    {
        List<String> result = new Vector<String>();

        logger.log(Level.INFO, AUTHENTICATION_INTERFACE + " trying to get groups of user  " + usid);

        try
        {
            IUserAuthenticationService validationService = SecurityUtil.lookupUserValidationService(AUTHENTICATION_INTERFACE);
            result = validationService.getGroups(usid);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return result;
    }
}

2.- Configuring Glassfish to use the custom Realm.

Finally we will put everything together. We must do the following steps:

  1. Be sure to have your server stopped
  2. Create a jar file with our com-booreg-security project. We must put this jar file on $glassfish_install_dir\glassfish\domains\MyDomain\lib.
  3. Write the following line at login.conf file located at  $glassfish_install_dir\glassfish\domains\MyDomain\config

    MyCustomModule {
    com.booreg.auth.MyCustomModule required;
    };
    
  4. Start the server and open admin console, normally at http://localhost:4848
  5. Go to section Configurations → server-config→ Realms and create a new realm that we will call MyCustomRealm. It must have the following properties:
    • Realm Name: MyCustomRealm
    • Class Name: com.booreg.auth.MyCustomRealm
    • Add an additional property called jaas-context with value MyCustomModule. This property will make thw bridge between the configured realm in Glassfish and our authentication module.

    You can also modify the domain.xml file to add realm configuration if you don’t like to use the admin console.

    Creating the custom realm

  6. Re-start de server. Now you should see in your log file the log information about your realm that we wrote in MyCustomRealm class:
    INFO: MyCustomRealm started.
    INFO: MyCustomRealm: Authentication done by checking user at table USERS on database
    INFO: MyCustomRealm authentication uses jar file com-booreg-security.jar located at $domain/lib folder
    INFO: jaas-context: MyCustomModule
    INFO: SEC1115: Realm [MyCustomRealm] of classtype [com.booreg.auth.MyCustomRealm] successfully created.
    

3.- Associate the Web project to the custom Realm.

Finally we must tell to our application to use MyCustomRealm as a security realm. To do this me must modify our web.xml file in our WAR project and add the following lines:

<login-config>
<auth-method>FORM</auth-method>
<realm-name>MyCustomRealm</realm-name>
</login-config>

and add the following lines at glassfish-web.xml file:

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

Notice that in several steps we have made reference to a user group called allUsers. In this example I haven’t made use of group or roles related to our security configurations. I have simplified the code to use only a predefined group and role called allUsers. Java EE allows to restrict by code access to certain sensible parts of your code using roles. But in this case I wanted to focus on custom realm and not on roles. That maybe will be a question in future posts.

Summary

So we have arrived finally at the end. Although it seems a difficult task at first sight, knowing the steps and having an authentication method already programmed, creating a custom realm is a question of a few minutes. Let’s take a quick look to the steps to follow:

  1. You must have first created all the infrastructure needed to run the validation method. That is: you need a login form, your users and group tables. And you need the authentication procedure to decide if a user can log in into your application or not.
  2. You must have two classes descending from AppservRealm and AppservPasswordLoginModule and overriding the specified method to use your own authentication procedure.
  3. You must configure Glassfish changing login.conf file and domain.xml file (or use the admin console to change domain.xml file).

And that’s all. You can find the source code for this project at this link.

Cardedeu, July, 8th, 2013.

Advertisements

15 thoughts on “How to create a custom realm in Glassfish 3.1.2.2

  1. Thanks very much for this! I managed to get this going with Glassfish 4 after a small tweak to the persistence name to make it match the persistence.xml that was auto generated. I’m only a Java beginner, but with this as a guide I have learnt a lot.

  2. Hi, I have just a question about user validation method.

    instead using jdbc (SQL query) to validate user inside LoginModule , you have used an ejb to make a validation externally to LoginModule, thereby your ejb ” UserAuthenticationService” is excluded from your security realm (No Security annotation). do you think that missing annotation can be a security issue ?

    • Wow, very good question. That’s a point I have always asked myself. I am not used to annotate EJBs with security annotations because I feel it unnecessary. Our applications, by now, does’nt need this level of security. So, this EJB is being executed outside the realm of authentication, you’re right. But, in any case, if you decide to do this validation inside LoginModule you will execute it also outside this security realm, so what’s the difference ? Or, if we have security annotations on EJB what are the restrictions to have if the user hasn’t been validated yet ? What kind of security can he have here? Do we have to create another security realm to execute this piece of code ? I don’t think so. I think there is no sense to have security just in the point that you want to validate user security. It’s a case of the dog chasing its tail. It’s like to put a lock to access to another lock.

      Well. I am not a security expert, so maybe I am wrong, but in this point security has no sense because you are just validating user’s security. It’s more important, in my opinion, to protect net security from sniffers having the server secured with https access method.

      Many thanks for this question and for your feedback.

  3. Thank u very well for this tutorial ! I need it to implement a “stay logged in” feature . This is exactly what I was looking for

  4. Hello Dani Gisbert,

    thanks for your tutorial. It helped me a lot. I have a question.
    Recently, I found an article about comparing remote ejb agains other EE technologies. You can find it here:

    http://mjremijan.blogspot.sk/2014/09/which-is-faster-remote-ejbs-versus.html

    The result says, JAX RS is faster than remote EJB. I saw other articles saying JAX RS is twice as fast as remote EJB.
    I’m thinking about changing the remote ejb to no interface ejb and access it via REST. So basicaly the custom realm will be the REST client. I will protect the JAX RS resource with some custom headers and strings.

    What is your opinion on this? Do you think it is a good idea or good practice?

    • Hi Erik.

      Thank you very much for your feedback. Really an interesting article you’ve pointed here. I’m not really sure about the comparison speed of JAX-RS vs EJB Remote. I had the opposite idea that this article says, but I’ve never had made a comparison.

      In any case, the idea you are proposing is an interesting idea. When I wrote this article I did’nt know anything about REST Web Services. I discovered them later, specially when I started to make Android programs. I think REST WebServices (in Java EE, JAX-RS) are the de-facto standard communication way between applications. So I think it could be possible to use that to call web services to check user account. The only problem that comes to my mind is how will you manage o call to a webservice to make user account check without compromising the security of your server. You will have to make an unsecure call to this WS , or with a master username / password ? What Realm will you use for that ? If wou make a Ws that checks username / password this Ws will be opened to anybody that could know the existence of that WS ?

      Thanks again for your comments.

      • Dani,

        if I wanted to protect my server I would do it the same way as OAuth works. For example. I would generate some random string with UUID class. This key is same for a server and a client (let’s call it client ID). In order to access authorization URL I need a secure token. In order to get that token I need to access a public URL and post the client ID in json for example. Server generates a token which expires in 8 hours.

        Every time I need to authorize a client I will post the secure token as well. Server verifies the token, if valid authorize client, if not throw an exception. If a token expires, the client application will send a request for a new token. I’m not an expert, I know just a basic principle of OAuth.

        In a reply to a laminos question, why didn’t you use security annotation, you said we are just validating the user security. So basically we are doing the same with JAX RS. Maybe we don’t need to protect the authorization URL. Maybe it is enoght to post just some randome string which is same for client and server.

        Even the remote EJB can be invoked from outside of server using CORBA as mentioned here:

        http://stackoverflow.com/questions/18219621/access-to-an-ejb-remotely-from-a-different-server

        But I don’t want to go any deeper in this problematic. I was just curious about your opinion. I don’t see many tutorials such as yours. This is very unique, because you described it in details. Other tutorials about custom realms just describe the process of extending AppservPasswordLoginModule and AppservRealm and that’s all.

        Thanks again, and pls continue posting good examples.

      • Thanks Erik again for your comments and shared thoughts.

        About your OAuth-like solution maybe you could use already made implementations like pointed here: http://stackoverflow.com/questions/10296681/is-there-an-oauth-2-0-provider-implementation-in-java-not-oauth-client It seems that there is a good Apache implementation called Apache Oltu. Unfortunatelly I can’t have time to investigate and use it, but it seems promising.

  5. At the point I implement the MyCustomModule-class I get an error “The import com.sun.appserv cannot be resolved”.

    I manually add the tools.jar to the project but it didn’t help. :/

    What I forgot?

  6. Can’t find com.sun.appserv.security.AppservPasswordLoginModule; in security.jar !! Where can i find it ?

  7. I am able to do the authentication using custom realm configuration in glassfish. But I want to get the username ( j_username) in my error file (error.jsp) which is defined in the web.XML file so that I can display the failed login attempts to the user.

  8. Thank you. Your step by step still working in payara 4.1.1.

    For those having problem with AppservPasswordLoginModule they need to get jar glassfish-ee-api.jar at same folder where you get the security.jar.

    Regards

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s