Secure Your JSF Application with JAAS

how-to

In our experience as Java specialists, we noticed that a large number of companies prefer building their custom security model to using an already existent one. Perhaps an explanation for this would be that many developers aren’t well up on Java technologies and their strengths.

An advantage of using frameworks that already exist is that these are already tested and adopted by some companies.

What Is JAAS?

JAAS (Java Authentication and Authorization Service) is the security model proposed by Sun Microsystems to secure Java applications. The entire Java API is built on the JAAS security model.

By examining the canRead() method in the java.lang.File class, we find the following lines of code:

SecurityManager security = System.getSecurityManager();
if (security != null) {
   security.checkRead(path);
}

Thus, when a new File object is created, if we call the canRead() method, this will return true or false depending on whether we can read the specified file or not. This operation is known as authorization operation.

Authentication is the operation which verifies if the credentials (in most cases, the username and password) provided by a user or a system are valid.

To enable security in Java, you have to specify the -Djava.security.manager parameter for the virtual machine. The basic element of JAAS is the Subject object, which can be a person or a service. This subject contains a list of principals, public credentials and private credentials.

A Principal is a piece of information that belongs to a user who is logged in to the system. For example, a user has a name Principal (e.g., John Doe) and a SSN Principal (e.g., 1234). The principals are associated with the subject only if the authentication succeeds. A principal must implement the java.security.Principal and java.io.Serializable interfaces. Public and private credentials are represented by any class because they are not part of JAAS.

For more information about JAAS, you can read the JAAS Reference Guide.

Implementing The RBAC Security Model Using JAAS

RBAC is a role-based security model. When defining an RBAC model, the following conventions are useful:

  • S = Subject – A person or an automated agent
  • R = Role – A job function or title which defines a level of authority
  • P = Permissions – An approval of a mode of access to a resource
  • SE = Session – A mapping involving S, R and/or P
  • SA = Subject Assignment
  • PA = Permission Assignment
  • RH = Partially ordered role hierarchy. RH can also be written as ≥ (The notation x≥y means that x inherits the permissions of y.)
  • A subject can have multiple roles.
  • A role can have multiple subjects.
  • A role can have multiple permissions.
  • A permission can be assigned to multiple roles.

A Simple RBAC Model Definition

Suppose we have a medical application containing the following elements:

  • Users: admin, John, Marry, Bill
  • Roles: admin, doctor, secretary, patient
  • Permissions: all permissions, create medical records, view medical records, update medical records, delete medical records, create appointment, update appointment, delete appointment

Definition of Roles

  • admin role has the following permissions: all permissions
  • doctor role has the following permissions: create medical records, view medical records, update medical records
  • secretary role has the following permissions: create appointment, update appointment
  • patient has the following permissions: view medical records

Definition of Users

  • admin has admin role
  • John has doctor role
  • Marry has secretary role
  • Bill has patient role

The RBAC model is a good security model, but it might prove problematic when dealing with a lot of users because it is then possible to have an explosion of roles. This means that, for each user in the system, we must create a separate role and all those roles can be very hard to maintain.

When using the RBAC model with JAAS, we have the following associations:

  • Subject – the user
  • Principal – the user role
  • Permission – the role permission
  • Credentials – the information about the user (the id from the database, the user name, etc.)

Preparing the Test Environment

For our demo application, we used Tomcat 6.0.x as a web container, MySQL 5.0 as a database, Java 6 and the following frameworks:

  • Hibernate 3.3
  • Spring framework 2.5.6
  • JSF 2.0 – Oracle implementation
  • Facelets 1.1.9
  • Maven – for the build

Tomcat Configuration

Add the lines below to the end of the catalina.policy file. (You can find this file in the conf directory of your Tomcat installation.)

grant codeBase "file:${catalina.home}/webapps/jjwa/-" {
   permission java.util.PropertyPermission "*", "read,write";
   permission javax.security.auth.AuthPermission "modifyPrincipals";
   permission javax.security.auth.AuthPermission "modifyPublicCredentials";
   permission javax.security.auth.AuthPermission "modifyPrivateCredentials";
   permission javax.security.auth.AuthPermission "createLoginContext.*";
   permission javax.security.auth.AuthPermission "doAs";
   permission javax.security.auth.AuthPermission "doAsPrivileged";
   permission javax.security.auth.AuthPermission "getSubject";
   permission java.security.SecurityPermission "setPolicy";
   permission java.security.SecurityPermission "getPolicy";
   permission java.lang.RuntimePermission "accessClassInPackage.*";
   permission java.lang.RuntimePermission "getProtectionDomain";
   permission java.lang.RuntimePermission "loadLibrary.*";
   permission java.lang.RuntimePermission "modifyThread"; 
   permission java.lang.RuntimePermission "createClassLoader"; 
   permission java.lang.RuntimePermission "accessDeclaredMembers"; 
   permission java.net.SocketPermission "*:*", "accept,connect,resolve";  	 
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
   permission java.lang.RuntimePermission "setContextClassLoader";
   permission java.lang.RuntimePermission "getClassLoader";
   permission java.io.FilePermission "<>", "read";
};

When you start the Tomcat container, use the following command: catalina.bat run -security (for Windows) or sh catalina.sh run -security (for Linux).

Don’t forget to define CATALINA_HOME as a system variable.

Database Configuration

To create the database, execute the following scripts:

ddl.sql

DROP DATABASE IF EXISTS jjwa;
CREATE DATABASE jjwa;
USE jjwa;

CREATE TABLE USERS (
  ID bigint(6) unsigned NOT NULL auto_increment,
  USERNAME  varchar(256) NOT NULL,
  PASSWORD  varchar(256) NOT NULL,
  IS_ONLINE varchar(1) NOT NULL,
  FIRST_NAME varchar(256) NOT NULL, 
  LAST_NAME varchar(256) NOT NULL,
  GENDER    varchar(1) NOT NULL,
  BIRTH_DATE date not null,
  COUNTRY varchar(256) NOT NULL,
  PROVINCE varchar(256) NOT NULL,
  CITY varchar(256) NOT NULL,
  CREATION_DATE timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (ID)
) ENGINE=InnoDB;

CREATE TABLE ROLES (
  ID bigint(6) unsigned NOT NULL auto_increment,
  NAME varchar(128) NOT NULL,
  CREATION_DATE timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (ID)
) ENGINE=InnoDB;

CREATE TABLE PERMISSIONS (
  ID bigint(6) unsigned NOT NULL auto_increment,
  NAME varchar(512) NOT NULL,
  DESCRIPTION varchar(512) NOT NULL,  
  CREATION_DATE timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (ID)
) ENGINE=InnoDB;

CREATE TABLE PERM_ACTIONS (
  ID bigint(6) unsigned NOT NULL auto_increment,
  NAME varchar(512) NOT NULL,
  MASK int(6) NOT NULL,  
  CREATION_DATE timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (ID)
) ENGINE=InnoDB;

CREATE TABLE USERS_ROLES (
  USER_ID bigint(6) unsigned NOT NULL,
  ROLE_ID bigint(6) unsigned NOT NULL,     
  PRIMARY KEY  (USER_ID, ROLE_ID),
  constraint FK_USERS_ROLES_U foreign key (USER_ID) references USERS (ID),
  constraint FK_USERS_ROLES_R foreign key (ROLE_ID) references ROLES (ID)
) ENGINE=InnoDB;

CREATE TABLE ROLES_PERMISSIONS (
  ROLE_ID bigint(6) unsigned NOT NULL,
  PERMISSION_ID bigint(6) unsigned NOT NULL,     
  PRIMARY KEY  (ROLE_ID, PERMISSION_ID),
  constraint FK_ROLES_PERMISSIONS_P foreign key (PERMISSION_ID) references PERMISSIONS (ID),
  constraint FK_ROLES_PERMISSIONS_R foreign key (ROLE_ID) references ROLES (ID)
) ENGINE=InnoDB;

CREATE TABLE PERMS_ACTIONS (
  PERMISSION_ID bigint(6) unsigned NOT NULL,
  ACTION_ID bigint(6) unsigned NOT NULL,    
  PRIMARY KEY  (PERMISSION_ID, ACTION_ID),
  constraint FK_PERMS_ACTIONS_P foreign key (PERMISSION_ID) references PERMISSIONS (ID),
  constraint FK_PERMS_ACTIONS_A foreign key (ACTION_ID) references PERM_ACTIONS (ID)
) ENGINE=InnoDB;

dml.sql

insert into PERM_ACTIONS (ID, NAME, MASK) values (1, 'read', 1);
insert into PERM_ACTIONS (ID, NAME, MASK) values (2, 'create', 2);
insert into PERM_ACTIONS (ID, NAME, MASK) values (3, 'update', 4);
insert into PERM_ACTIONS (ID, NAME, MASK) values (4, 'delete', 8);

insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (1, 'USER_LIST', 'View users list');
insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (2, 'USER', 'View user profile');
insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (3, 'USER', 'Create a new user');
insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (4, 'USER', 'Update a user');
insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (5, 'USER', 'Delete a user');
insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (6, 'MY_ACCOUNT', 'Access my account');
insert into PERMISSIONS (ID, NAME, DESCRIPTION) values (7, 'MY_ACCOUNT', 'Update my account');

insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(1, 1);
insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(2, 1);
insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(3, 2);
insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(4, 3);
insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(5, 4);
insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(6, 1);
insert into PERMS_ACTIONS(PERMISSION_ID, ACTION_ID) values(7, 3);

insert into ROLES(ID, NAME) values(1, 'admin');
insert into ROLES(ID, NAME) values(2, 'user');

insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 1);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 2);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 3);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 4);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 5);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 6);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(1, 7);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(2, 6);
insert into ROLES_PERMISSIONS(ROLE_ID, PERMISSION_ID) values(2, 7);

insert into USERS(ID, USERNAME, PASSWORD, IS_ONLINE, FIRST_NAME, LAST_NAME, GENDER, BIRTH_DATE, COUNTRY, PROVINCE, CITY)
values(1, 'admin', 'admin', 'N', 'John', 'Doe-Admin', 'M', '1982-07-31', 'Country1', 'Province1', 'City1');
insert into USERS(ID, USERNAME, PASSWORD, IS_ONLINE, FIRST_NAME, LAST_NAME, GENDER, BIRTH_DATE, COUNTRY, PROVINCE, CITY)
values(2, 'user', 'user', 'N', 'John', 'Doe', 'M', '1980-06-21', 'Country2', 'Province2', 'City2');

insert into USERS_ROLES(USER_ID, ROLE_ID) values (1, 1);
insert into USERS_ROLES(USER_ID, ROLE_ID) values (2, 2);

After that, update the following lines in the dao-context.xml file with your data (your database username and password). By default, the database username is root and the password is admin:

<property name="url" value="jdbc:mysql://localhost/jjwa" />
<property name="username" value="root" />
<property name="password" value="admin" />

The diagram below provides a visual overview of the database structure.

[image img='http://ixtendo.com/wp-content/uploads/2012/02/db-model.png' url='http://ixtendo.com/wp-content/uploads/2012/02/db-model.png' alt='jaas-db-model' align='none' lightbox='true']

Building the Project

To build the project, you have to install Maven and call mvn clean package.

Running the Application

Start the Tomcat container using the following command: catalina.bat run -security (for Windows) or catalina.sh run -security (for Linux). The application can be accessed using the following address: http://localhost:8080/jjwa/.

The accounts that can be used for the testing are:

  • admin (username = admin, password = admin) – can access all pages
  • user (username = user, password = user) – can view all pages except the users list page

How It Works

[image img='http://ixtendo.com/wp-content/uploads/2012/02/request-processing-670x356.png' url='http://ixtendo.com/wp-content/uploads/2012/02/request-processing.png' alt='jaas-db-model' align='none' lightbox='true']

To show you how this application works, we created some sequence diagrams which illustrate how an http request is processed and how security is applied.

Suppose we call the following link in the browser: http://localhost:8080/jjwa/admin_page.html/.

When this request is received by the web container, the doFilter() method in SecurityFilterListener is called. This method ensures that the Subject is set on the user session context. It also checks if the Subject principals are empty. If so, then an anonymous role (which is the default role) is added to the user principals list.

After that, the request is sent to the FacesServlet service() method for processing. In the JSF render response phase, the getUsers() method in AccountController is called to render admin_page.xhtml.

Below is shown the body of this method.

public List getUsers() {
    try {
        return execute(new Callable>() {
            public List call() throws Exception {
                return userService.getAll();
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

The execute(callable) method tries to execute the getAll() method in the UserServiceImpl class. To verify if the user has the necessary rights to execute this method, we created an aspect using Spring AOP (see the service-context.xml file).

Thus, when a method of any class in the com.jsource.jjwa.service package is executed, the Object checkAuthorization(ProceedingJoinPoint pjp) method in the AuthorizationCheckerExecutor class is invoked. The implementation of this method is shown below.

public Object checkAuthorization(ProceedingJoinPoint pjp) throws Throwable {
    MethodSignature o = (MethodSignature) pjp.getSignature();
    SecurityConstraint sc = o.getMethod().getAnnotation(SecurityConstraint.class);
    if (sc != null) {
        Permission[] permissions = Permissions.getPermissionsByTypes(sc.permissions());
        if (permissions != null) {
            if (SecurityUtil.hasPermissions(permissions)) {
                return pjp.proceed();
            } else {
                throw new SecurityViolationException("You have no rights to do this operation.");
            }
        }
    }
    return pjp.proceed();
}

As you can see in the example above, first we checked to see if the user had the required rights to execute the target method. If he hasn’t the necessary roles, then a SecurityViolationException is thrown.

We chose to put the security verification in the service tier because, when you change the client tier (for example, when you want to implement a Java Swing client in this application), security is maintained and all you have to do is manage the security exceptions properly.

Observation

Since the project was made a long time ago, it is possible to encounter problems during the compilation because the JBoss mvn repository address was changed in the meantime. To fix this problem, please do the following modifications in the pom.xml file:

  1. (Mandatory) Delete all the repositories which contain jboss.org in their URLs
  2. (Mandatory) Add this repository under the <repositories> tag:
    <repository>
       <id>repository.jboss.org</id>
       <name>jboss.org Repository for Maven</name>
       <url>https://repository.jboss.org/nexus/content/repositories/thirdparty-releases</url>
       <layout>default</layout>
    </repository>
    
  3. (Optional, but recommended) Comment the tags <sourceDirectory> and <directory> which are located between the <build> tags. The final build will be located under the target folder in the project.

Enjoy!

Because of limited time, we can't offer support for the freebies, guys. However, we do hope that the item documentation and/or commented code will be helpful to you.

Thanks for your visit and enjoy the freebie! :)

Share:

12 Responses

  1. What’s up everyone, it’s my first pay a visit at this site, and piece
    of writing is actually fruitful for me, keep up posting such content.

  2. Fred says:

    Hi,
    This is a great example. Thank you so much for making it available. I got it to work very easy. Question: Would it be difficult to get this to work with JBoss or WebLogic? Would I somehow have to incorporate the contents of the catalina.policy file if I want to try to get this working for JBoss or WebLogic? Thanks and keep up the great work.

  3. richa says:

    Ok. Now I dont any exceptions..I am able to run the application the way u said. But i have few questions :- 1) If I want to make “user” to see the Admin’s page (the page which admin sees when he logs in), what should I do ? I mean where exactly is that particular checking done ? in catalina.policy code which we added ? pl tell me how can I make user see the admin page ? That would be interesting to know.. 2) I have deployed jjwa.war in web apps of tomcat. I have set up the database the way u said. So when I access the jjwa application, does it use the mysql database tables ? the example which u described about doctor, patient etc is just to explain or can we really make use of it with this sample code ?

    • ixtendo says:

      Hi,

      Regarding Maven, first you have to download and unzip it. After that, you have to navigate to the project folder where the pom.xml file is located. Here, you have to call the mvn clean package. If you don’t have Maven set in your path, then you’ll have to invoke it like this: /usr/share/mvn/bin/mvn.sh clean package. (I assumed that you have Maven installed in the /usr/share/ folder). The .war archive will be placed in the target folder located in the same directory as pom.xml.

      Answers to your questions:

      1. The security check is done in two places: one is the MVC layer (see the SecurityController class) and the second is the service layer, through an AOP join point (see the AuthorizationCheckerExecutor class).
      2. When you access the application, the user rights are read from the MySQL database. The example with the doctor and the patient was given just to explain the RBAC security model.

      Regards.

  4. richa says:

    Hi thanks for such a great example. Could u please elaborate little how to perform this “To build the project, you have to install Maven and call mvn clean package. “. I have maven installed but new to it so dont know how to build the project with maven. I am using ubuntu 12.04. Could u pl tell me how to build the project step by step ?

    Thanks a lot..

    • richa says:

      Hey I directly deployed the jjwa.war file in web-apps dir of tomcat. When I try to login to the page in browser with admin-admin or with user-user (uname-password), I get java.lang.nullpointer exaception. Could you pl say why ? I have changed folllowing in my dao-context.xml

      any idea why it gives me exception ?

  5. msteinbe says:

    Hi, thank you for your complete example.
    I also have a question:
    In the filter (securityFilterListener) you save the new subject in session, under the key: javax.security.auth.subject.
    In the authentication controller, you get the subject saved in session with the method:Subject.getSubject(AccessController.getContext())
    It works fine, I just don’t understand how you find out and didn’t find any documentation.
    Can you explain me how it works and where to find some documentation ?
    Thanks !

    • ixtendo says:

      Hi,

      I found that key by examining the Tomcat source code. To understand how Tomcat uses the respective key, you’ll have to dig in the Tomcat source code. That’s because I debugged that code a few years ago to understand how it worked and, unfortunately, don’t remember it anymore.

      Regards!

  6. Edwin F. López says:

    Hi and thanks. This proves really useful.
    One quick question though, how different this would be for glassfish?
    Thanks

    • ixtendo says:

      To deploy this on GlassFish, you must see where this application server keeps the Subject. In Tomcat, the subject is kept under a specific key (you can find the name of the key in the secure filter).

  7. Iker says:

    This is a great and complete example of JAAS+JSF. Good job! Thank you!

    • admin says:

      Thank you, Iker! I’m glad that you found this application useful. I will try to release a new and more complex version soon.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>