Separating Roles and Permissions in Spring Security

This article is based on Spring in Practice, to be published August-2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code ‘java40beat’ and get 40% discount on eBooks and pBooks]

Technique: Separate Roles and Permissions

Introduction

The goal behind separating roles and permissions is to avoid embedding security policy decisions in the code. Such decisions should be set at runtime since they vary across customers, they vary over time, and sometimes they need to be changed immediately (for example, in response to a security breach).

The first rule breaks when somebody decides that teaching assistants, parents, faculty trainers, accreditors, or any number of other roles should gain access or that one of the roles should lose access (for instance, faculty-only forums). The roles may be different for different customers using the software, and many of the roles may not even make any sense for some customers.

The second rule is more resilient in the face of such changes since in essence it says that a user gets a given forum if he has read access to forums generally. The rule isn’t perfect—we may decide that there isn’t any such thing as “read access to forums generally” (that is, access exists on a forum-by-forum basis)—but clearly it’s much more flexible, especially if we can establish the relationship between roles and permissions outside the code itself. And we can certainly do that.

So as a general rule, prefer permission-based rules to role-based rules. There are exceptions to this rule, but it holds as a general rule.

Spring Security 3 appears schizophrenic on the issue of separating roles and permissions in the manner shown above. The interface underlying ROLE_STUDENT, PERM_READ_FORUMS and so forth is called GrantedAuthority, and this sounds like a fancy way of saying “permission” rather than “role.” But the examples in the Spring Security reference documentation tend to treat granted authorities as roles, and even the hasRole() and hasAnyRole() predicates steer us toward using roles directly, which is at best a questionable practice for the reasons already given.

Figure 1 User schema that separates roles from permissions.Apparent schizophrenia aside, Spring Security makes it easy to do the right thing. The sample code, for instance, uses a custom UserDetailsService backed by the user/role/permission schema in figure 1.2 The sip07-schema-mysql.sql script (available here) contains this schema, but it’s just an example. Even if you’re using the JdbcDaoImpl instead of a custom UserDetailsService, you can take advantage of the Spring Security group schema to separate roles and permissions.

Besides the schema, we also need some sample data so we can actually test the security configuration. Figure 2 shows the roles and permissions that each of our sample users has, as contained in the sip07-data-mysql.sql script.

Figure 2 Users, roles, and permissions for the sample applicationThat’s it for our source code changes. Now we need to “activate” the security annotations. To do that, we add a single line to the beans-security.xml configuration:

<global-method-security pre-post-annotations="enabled" />

We’ve enabled Spring Security’s pre- and post-annotations, disabled by default, since they allow us to use SpEL to define access rules in an elegant fashion. This is the preferred approach in Spring Security 3. There are, however, a couple of other options, which we list for completeness:

jsr250-annotations=”enabled”: Activate the standard JSR 250 security annotations. Though these are standard, they support only simple role-based rules and aren’t nearly as powerful as Spring Security’s pre/post annotations. These are disabled by default.

secured-annotations=”enabled”: Support for Spring’s legacy @Secured annotation. Originally superseded by the JSR 250 @RolesAllowed annotation and now by the Spring Security @PreAuthorize annotation. @Secured is disabled by default.

That’s annotation-based configuration. To try out the security annotations, try the following:

Start up the application and click the forums link. Spring Security will force a login because the call to getForums() requires the PERM_READ_FORUMS permission.

Log in as user daniel/p@ssword. He has just the student role.

Go into one of the forums and try to block a message. You should get an error message in a dialog box because the ForumServiceImpl.setMessageVisible() method requires the PERM_ADMIN_MESSAGES permission, which the student role does not have.

Try the same thing with editing and deleting messages. You’ll be able to get the edit page and the delete confirm box, but there will be an error message when you try to actually save the edit or confirm the deletion, because the student role doesn’t have the required PERM_UPDATE_MESSAGES and PERM_DELETE_MESSAGES permissions.

Log out, and then log back in under juan/p@ssword. User juan has the admin role. Try the same operations. You should be able to execute all of them, because the admin role has the required permissions.

Summary

We created authorization rules and applied them to Java methods. We showed you why we used a hasRole() predicate to check for a permission, since roles and permissions aren’t the same thing. A role typically entails a set of permissions. The release engineer role, for example, might have permission to deploy software packages to servers.

He is Founder and Chief Editor of JavaBeat. He has more than 8+ years of experience on developing Web applications. He writes about Spring, DOJO, JSF, Hibernate and many other emerging technologies in this blog.