7 min read

(For more resources related to this topic, see here.)

So we’ve got this thing for authentication and authorization. Let’s see who is responsible and what for.

There is an AccessDecisionManager, which, as the name suggests, is responsible for deciding whether we can access something or not; if not, an AccessDeniedException or InsufficientAuthenticationException is thrown.

AuthenticationManager is another crucial interface. It is responsible for confirming who we are.

Both are just interfaces, so we can swap our own implementations if we like.

In a web application, the job of talking with these two components and the user is handled by a web filter called DelegatingFilterProxy, which is decomposed into several small filters. Each one is responsible for a different thing, so we can turn them on, off, or put our own filters in between and mess with them anyway we like. These are quite important, and we will dig into them later. For the big picture, all we need to know is that these filters take care of all the talking, redirect the user to the login page (or an access-denied page), and save the current user details in an HTTPSession.

Well, the last part, while true, is a bit misleading. User details are kept in a SecurityContext object, which we can get a hold of by calling SecurityContextHolder.getContext(), and which in the end is stored in HTTPSession by our filters. But we had promised a big picture, not the gory details, so here it is:

Quite simple, right?

If we have an authentication protocol without login and password, it works in a similar way. We just switch one of the filters, or the authentication manager, to a different implementation. If we don’t have a web application, we just need to do the talking ourselves.

But this is all for web resources (URLs). What is much more interesting and useful is securing calls to methods. It looks, for example, like this:

@PreAuthorize(["isAuthenticated() and hasRole('ROLE_ADMIN')"])
public void somethingOnlyAdminCanDo() {
}

Here, we decided that somethingOnlyAdminCanDo will be protected by our AccessDecisionManager and that the user must be authenticated (not anonymous) and has to have an admin role. Can a user be anonymous and have an admin role at the same time? In theory, yes, but it would not make any sense. Because it’s much cheaper to check if he is authenticated and stop right there. We see a bit of optimization in here. We could drop the isAuthenticated() method and the behavior wouldn’t change.

We can put this kind of annotation on any Java method, but our configuration and mechanism to fire up the security will depend on the type of objects we are trying to protect.

For objects declared as Spring beans (which is a short name for anything defined in our Inversion of Control (IoC) configuration, either via XML or annotations), we don’t need to do much. Spring will just create proxies (dynamic classes) that take over calls to our secured methods and fire up AccessDecisionManager before passing the call to the object we really wanted to call. For objects outside of the IoC container (anything created with new or just code not defined in Spring context), we can use the power of Aspect Oriented Programming (AOP) to get the same effect. If you don’t know what AOP is, don’t worry. It’s just a bit of magic at the classloader and bytecode level. For now, the only important thing is that it works basically in the same way. This is depicted as follows:

We can do much more than this, as we’ll see next, but these are the basics.

So, how does the AccessDecisionManager decide whether we can access something or not?

Imagine a council of very old Jedi masters sitting around a fire. They decide whether or not you are permitted to call a secured method or access a web resource. Each of these masters makes a decision or abstains. Each of them can consult additional information (not only who you are and what you want to do, but every aspect of the situation). In Spring Security, those smart people are called AccessDecisionVoters, and each of them has one vote.

The council can be organized in many different ways. It has one voice, and so it may make the decision based on a majority of votes. It may be veto-based, where everything is allowed unless someone disagrees. Or it may need everyone to agree to grant access, otherwise access is denied. The council is the AccessDecisionManager, and we have three implementations previously mentioned out of the box. We can also decide who’s in the council and who is not. This is probably the most important decision we can make, because this will decide the security model that we will use in our application.

Let’s talk about the most popular counselors (implementations of AccessDecisionVoter).

  • Model based on roles (RoleVoter): This guy makes his decision based on the role of the user and the required role for the resource/method. So if we write @PreAuthorize(“hasRole(‘ROLE_ADMIN’)”), you better be a damn admin or you’ll get a no-no from this guy.
  • Model based on entity access control permissions (AclEntryVoter): This guy doesn’t worry about roles. He is much more than that. Acl stands for Access Control List, which represents a list of permissions. Every user has a list of permissions, possibly for every domain object (usually an object in the database), that you want to secure.

    So, for example, if we have a bank application, the supervisor can give Frank access to a single specific customer (say, ACME—A Company that Makes Everything), which is represented as an entity in the database and as an object in our system. No other employee will be able to do anything to that customer unless the supervisor grants that person the same permission as Frank.

    This is probably the most scrutinous voter we would ever use. Our customer can have a very detailed configuration with him/her. On the other hand, this is also the most cumbersome, as we need to create a usable graphical interface to set permissions for every user and every domain object. While we have done this a few times, most of our customers wanted a simpler approach, and even those who started with a graphical user interface to configure everything asked for a simplified version based on business rules, at the end of the project.

    If your customer describes his security needs in terms of rules such as “Frank can edit every customer he has created but he cannot do anything other than view other customers”, it means it’s time for PreInvocationAuthorizationAdviceVoter.

  • Business rules model (PreInvocationAuthorizationAdviceVoter): This is usually used when you want to implement static business rules in the application. This goes like “if I’ve written a blog post, I can change it later, but others can only comment” and “if a friend asked me to help him write the blog post, I can do that, because I’m his friend”. Most of these things are also possible to implement with ACLs, but would be very cumbersome.

    This is our favorite voter. With it, it’s very easy to write, test, and change the security restrictions, because instead of writing every possible relation in the database (as with ACL voter) or having only dumb roles, we write our security logic in plain old Java classes. Great stuff and most useful, once you see how it works.

    Did we mention that this is a council? Yes we did. The result of this is that we can mix any voters we want and choose any council organization we like. We can have all three voters previously mentioned and allow access if any of them says “yes”. There are even more voters. And we can write new ones ourselves. Do you feel the power of the Jedi council already?

    Do you feel the power of the Jedi council already?

Summary

This section provides an overview of authentication and authorization, which are the principles of Spring security.

Resources for Article :


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here