Running your first Security API code
Security is one of the top concerns when you build an enterprise application. Luckily, the Java EE platform now has this API that handles many of the enterprise requirements in a standardized way.
In this recipe, you will learn how to define roles and give them the right authorization based on rules defined in the methods that manage sensitive data.
Getting ready
We start by adding our dependencies to the project:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.tomee</groupId> <artifactId>openejb-core</artifactId> <version>7.0.4</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency>
How to do it...
- We first create a
User
entity:
@Entity public class User implements Serializable{ @Id private Long id; private String name; private String email; public User(){ } public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } //DON'T FORGET THE GETTERS AND SETTERS //THIS RECIPE WON'T WORK WITHOUT THEM }
- Here, we create a class to store our security roles:
public class Roles { public static final String ADMIN = "ADMIN"; public static final String OPERATOR = "OPERATOR"; }
- Then, we create a stateful bean to manage our user operations:
@Stateful public class UserBean { @PersistenceContext(unitName = "ch01-security-pu", type = PersistenceContextType.EXTENDED) private EntityManager em; @RolesAllowed({Roles.ADMIN, Roles.OPERATOR}) public void add(User user){ em.persist(user); } @RolesAllowed({Roles.ADMIN}) public void remove(User user){ em.remove(user); } @RolesAllowed({Roles.ADMIN}) public void update(User user){ em.merge(user); } @PermitAll public List<User> get(){ Query q = em.createQuery("SELECT u FROM User as u "); return q.getResultList(); }
- Now, we need to create an executor for each role:
public class RoleExecutor {
public interface Executable {
void execute() throws Exception;
}
@Stateless
@RunAs(Roles.ADMIN)
public static class AdminExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}
@Stateless
@RunAs(Roles.OPERATOR)
public static class OperatorExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}
}
- And finally, we create a test class to try our security rules.
Our code uses three test methods: asAdmin()
, asOperator()
, and asAnonymous()
.
- First, it tests
asAdmin()
:
//Lot of setup code before this point @Test public void asAdmin() throws Exception { adminExecutor.run(() -> { userBean.add(new User(1L, "user1", "[email protected]")); userBean.add(new User(2L, "user2", "[email protected]")); userBean.add(new User(3L, "user3", "[email protected]")); userBean.add(new User(4L, "user4", "[email protected]")); List<User> list = userBean.get(); list.forEach((user) -> { userBean.remove(user); }); Assert.assertEquals("userBean.get()", 0, userBean.get().size()); }); }
- Then it tests
asOperator()
:
@Test public void asOperator() throws Exception { operatorExecutor.run(() -> { userBean.add(new User(1L, "user1", "[email protected]")); userBean.add(new User(2L, "user2", "[email protected]")); userBean.add(new User(3L, "user3", "[email protected]")); userBean.add(new User(4L, "user4", "[email protected]")); List<User> list = userBean.get(); list.forEach((user) -> { try { userBean.remove(user); Assert.fail("Operator was able to remove user " + user.getName()); } catch (EJBAccessException e) { } }); Assert.assertEquals("userBean.get()", 4, userBean.get().size()); }); }
- And, finally it tests
asAnonymous()
:
@Test public void asAnonymous() { try { userBean.add(new User(1L, "elder", "[email protected]")); Assert.fail("Anonymous user should not add users"); } catch (EJBAccessException e) { } try { userBean.remove(new User(1L, "elder", "[email protected]")); Assert.fail("Anonymous user should not remove users"); } catch (EJBAccessException e) { } try { userBean.get(); } catch (EJBAccessException e) { Assert.fail("Everyone can list users"); } }
Note
This class is huge! For the full source code, check the link at the end of the recipe.
How it works...
The whole point in this recipe is to do with the @RolesAllowed
, @RunsAs
, and @PermitAll
annotations. They define what operations each role can do and what happens when a user tries an operation using the wrong role.
There's more...
What we did here is called programmatic security; that is, we defined the security rules and roles through our code (the program). There's another approach called declarative security, where you declare the rules and roles through application and server configurations.
One good step up for this recipe is if you evolve the roles management to a source outside the application, such as a database or a service.
See also
- You can stay tuned with everything related to Security API at https://github.com/javaee-security-spec
- The source code of this recipe is at https://github.com/eldermoraes/javaee8-cookbook/tree/master/chapter01/ch01-security