ejb3

EJB Security Example

1. Introduction

Developers using the Java EE platform achieve security through its built-in security APIs. There are two approaches to securing enterprise java applications, more specifically Enterprise Java Beans. These approaches are the programmatic approach and the declarative approach. The programmatic approach is the one which is meant to be used when one wants to have more control over how the application should be secured. The declarative approach is used through the use of build in annotations, further provided these annotations fulfill’s the requirements of the application.

In this article, we will use the declarative approach of securing enterprise java beans to demonstrate how one can take advantage of the security APIs the Java EE platform provides.

2. Technologies used

  • Java SE 8
  • Java EE 7
  • Wildfly 12 Final
  • InteliJ 2016.2.5

3. Creating the project

Below is the first screen when creating a new Maven based inteliJ project.

Follow the below instructions as our first step in creating our application:

  1. Go to the main menu of the InteliJ IDE and select “New Project”
  2. Then select the type of project: in this example, we will be selecting “Maven”
  3. Then click Next

Step 1

Below is the second screen when creating a new Maven based IntelliJ project.

Follow the below instructions as our next step in creating our application:

  1. Specify the GroupId: in this example, we entered “com.javacodegeeks”
  2. Then the ArtifactId: in this example we entered “ejbscheduleexample”
  3. And then the version: in our example, we choose “1.0-SNAPSHOT”
  4. Then click Next

Step 2

Below is the last screen when creating a new Maven based inteliJ project.

Follow the below instructions as our last step in creating our application:

  1. Specify the project name: in our example, we choose “ejbschedulerexample”
  2. Specify the project location
  3. Then Click Finish

Step 3

4. Implementing the product entity Bean

The code snippet below is the product entity which is a basic representation of a product record we will be storing in our H2 database.

Product.java

package com.javacodegeeks.ejbsecurity;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String price;

    public Product() {
    }

    public Product(String name, String price) {
        this.name = name;
        this.price = price;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

        Product product = (Product) o;

        return new EqualsBuilder()
                .append(id, product.id)
                .append(name, product.name)
                .append(price, product.price)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(id)
                .append(name)
                .append(price)
                .toHashCode();
    }
}

The Entity bean makes use of the JPA APIs to register itself with the application container. The @Entity annotation tells the application container that this bean represents a record in the database table. Further, and if not specified the table name where the records will be stored will be the name of the Entity bean. The @Id annotation instructs the application container to use the id field as the primary key of the table.

The name and price field are the name of the product and the price of the product respectively. The accessor and mutators of the defined fields are there to use by the persistence framework to store and retrieve records from the database. We ‘ve also overridden the equals and hashCode which are used when equality of the bean being stored is a requirement.

5. Implementing the product service Bean

Below is the ProductService bean which we will be securing its defined methods, so that only authorized and authenticated users will be able to access them.

ProductService.java

package com.javacodegeeks.ejbsecurity;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.List;

@Stateless
public class ProductService {

    @PersistenceContext(unitName = "products")
    private EntityManager entityManager;

    @RolesAllowed({"User", "Admin"})
    public void addProduct(Product product) {
        entityManager.persist(product);
    }

    @RolesAllowed({"Admin"})
    public void deleteProduct(Product product) {
        entityManager.remove(product);
    }

    @PermitAll
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List getProducts() {
        TypedQuery query = entityManager.createQuery("SELECT p from Product as p", Product.class);
        return query.getResultList();
    }
}

In the above code snippet we create the Stateless ProductService, within it, we inject an Entity manager using our defined persistence unit. Further, we create 3 methods namely:

  • theaddProduct which is responsible for storing new product records into our database. This method is annotated with @RolesAllowed({"User", "Admin"})
    which basically means that only authenticated users or administrators will be able to access this method
  • thedeleteProduct which is responsible for deleting a product record from our database. This method is annotated with @RolesAllowed({"Admin"}) which means that only authenticated administrators will be able to access this method
  • thegetProducts which is responsible for fetching all products from our database. This method is annotated with @PermitAll which means that anyone will be able to access this method including unauthenticated users

6. The persistence configuration file

Below is the persistence unit definition configuration file which we created in order to configure the persistence framework.

persistence.xml

The persistence configuration file

7. Implementing the administrator handler bean

The code snippet below is the AdministratorHandler which we defined to mock a user with administrative privileges.

AdministratorHandler.java

package com.javacodegeeks.ejbsecurity;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RunAs;
import javax.ejb.Stateless;
import java.util.concurrent.Callable;

@Stateless
@RunAs("Admin")
@PermitAll
public class AdministratorHandler {
    public  V call(Callable callable) throws Exception {
        return callable.call();
    }
}

The above code snippet is a Stateless bean in which we use the @RunAs("Admin") annotation to tell the application container to instantiate and run this bean as a user with administrative privileges. The call method is a callback method which we use in our tests to execute service methods.

8. Implementing the user handler bean

The code snippet below is the UserHandler which we defined to mock a user with user privileges.

UserHandler.java

package com.javacodegeeks.ejbsecurity;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RunAs;
import javax.ejb.Stateless;
import java.util.concurrent.Callable;

@Stateless
@RunAs("User")
@PermitAll
public class UserHandler {
    public  V call(Callable callable) throws Exception {
        return callable.call();
    }
}

The above code snippet is a Stateless bean in which we use the @RunAs("User") annotation to tell the application container to instantiate and run this bean as a user with user privileges. The call method is a callback method which we use in our tests to execute service methods.

9. Implementing the product service integration test

Below is our implementation of the ProductService integration test. In which we use the Arquilian Framework to bootstrap and run our tests.

ProductServiceIT.java

package com.javacodegeeks.ejbsecurity;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.ejb.EJBAccessException;
import javax.inject.Inject;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;

@RunWith(Arquillian.class)
public class ProductServiceIT {

    @Inject
    private ProductService productService;

    @Inject
    private AdministratorHandler administratorHandler;

    @Inject
    private UserHandler user;

    @Deployment
    public static JavaArchive createDeployment() throws IOException {
        return ShrinkWrap.create(JavaArchive.class, "javaee-testing-security.jar")
                .addClasses(Product.class, ProductService.class, AdministratorHandler.class, UserHandler.class)
                .addAsManifestResource("META-INF/persistence.xml", "persistence.xml")
                .addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
    }

    @Test
    public void testAsAdministrator() throws Exception {
        administratorHandler.call(new Callable() {
            @Override
            public Product call() throws Exception {
                productService.addProduct(new Product("CAP", "$10"));
                productService.addProduct(new Product("Socks", "$5"));

                List products = productService.getProducts();
                Assert.assertEquals("List.size()", 2, products.size());

                for (Product book : products) {
                    productService.deleteProduct(book);
                }

                Assert.assertEquals("BookshelfService.getBooks()", 0, productService.getProducts().size());
                return null;
            }
        });
    }

    @Test(expected = EJBAccessException.class)
    public void testAsUser() throws Exception {
        user.call(new Callable() {
            @Override
            public Product call() throws Exception {
                productService.addProduct(new Product("Milk Shake", "$10"));
                productService.addProduct(new Product("cake", "$2"));

                List books = productService.getProducts();
                Assert.assertEquals("List.size()", 2, books.size());

                for (Product book : books) {
                    productService.deleteProduct(book);
                    Assert.fail("Users should not be allowed to delete");
                }
                Assert.assertEquals("BookshelfService.getBooks()", 2, productService.getProducts().size());
                return null;
            }
        });
    }

    @Test(expected = EJBAccessException.class)
    public void testAsUnauthenticatedUser() throws Exception {
        productService.addProduct(new Product("Pant", "$20"));
        Assert.fail("Unauthenticated users should not be able to add books");
        productService.deleteProduct(null);
        Assert.fail("Unauthenticated users should not be allowed to delete");
    }

    @Test
    public void testReadAsUnauthenticatedUser() throws Exception {
        List books = productService.getProducts();
        Assert.assertEquals("BookshelfService.getBooks()", 0, books.size());
    }
}

Line 19: We use the @RunWith(Arquillian.class) statement to tell the Junit framework that this is an Arquilian test.
Line 22 to 23: We inject the ProductService using the @Inject annotation.
Line 25 to 26: We inject the AdministratorHandler using the @Inject annotation.
Line 28 to 29: We inject the UserHandler using the @Inject annotation.
Line 31 to 37: Here we create a method annotated with the @Deployment annotation from the Arquilian framework. Further within it, we have code which creates a mock artifact which will contain all the code and configuration we need to deploy within our test.
Line 39 to 58: Here we use our AdministratorHandler to test the execution of the addProduct and getProducts method of the ProductService as an Administrator user.
Line 60 to 79: Here we test the same methods as our previous test but this time using the UserHandler to mock a user with user privileges. Our test expects an EJBAccessException because we try to access the getProducts without the correct user role.
Line 81 to 87: In this test we test our secured service methods as an unauthenticated user and we expect the test to throw an EJBAccessException.
Line 89 to 93: In this test, we test our non-secured service methods as an unauthenticated user.

10. Running final application

In order to run our final application use the maven command below in your terminal under the root directory of the project.

mvn clean install

11. Conclusion

In this article, we implemented an application which had an Entity bean as well as a Stateless service bean which was responsible for storing and retrieving as well as deleting data from the database through the Entity bean. Furthermore, we also discovered how to secure an Enterprise java bean using the Java EE security APIs. At the end of our demonstration, we made use of the Arquilian Framework together with the JUnit framework to test our secured enterprise bean.

12. Download the Source Code

That was the EJB Security Example.

Download
You can download the full source code of this example here: EJBSecurity

Georges Sofo

Georges Albert Sofo is a bachelors degree holder in software development, more specifically using Java technologies and following the OOP paradigm approach to software development. he has being working as a full time Software Engineer for more than 4 years, at companies ranging from telecommunication, consultancy, financial institution and educational type companies. His current focus lies in anything Java (Spring Framework preferably) on the backend and Javascript on the Frontend.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button