Spring Security was built to provide a full, robust, security module for all of your applications. It was also built with extensibility and flexibility in mind. If you were to look at all the classes working together to provide the whole solution, you would see lots of classes, each designed to handle one particular aspect of the whole security picture. This makes Spring Security a little difficult to grasp at first as you see all of these classes. However, the complete separation of concerns really opens up Spring Security so you can customize just the parts that you need..
For this article, we are going to change the definition of a User and how that User is loaded. For the loading of the User, we are going to use Hibernate. Spring Security comes with classes to load from a database where you simply configure the class with two queries and uses JDBC. There are also classes for LDAP, a flat file, or even in memory. In most cases, these built in classes will do the trick. But if you already have a Hibernate Repository created that loads the data, why not reuse it.
Our application is a mythical Order Process system where we have already created domain classes and Hibernate Repository classes. There are Customer, Order, Item and Role domain objects and corresponding repository classes. The full details of these classes aren't needed.
At this point I have an interesting option. I can use one of my domain classes like Customer to also act as my User object. Or I could just create another class to implement UserDetails interface. I say this is interesting because sometimes we have an application where when the user first logs in we load up their Customer data and need to store it for as long as needed in the “session”. The typical solution is to put this object into the HttpSession in say a web application. Now we can remove this solution and either use Customer as our User object, or have the new UserDetails class that we create to hold on to the Customer object.
In this article I am going to use the Customer object as our UserDetails object. The one downfall to this approach is that we will have to add some properties here that are security specific which might be considered polluting our domain model.
Here is our code the Customer object fully implementing the UserDetails interface.
Our implementation is very simple in terms of the overriding methods, we just return instance variable values, just like normal getters and setters. The interesting part is in the constructor that receives a list of Strings for the roles that the User/Customer has.
Now that we created our own UserDetails implementation, lets use it to customize the authorization piece of Spring Security. This consists of implementing another interface called UserDetailsService. This interface is actually even easier in that we only have to implement one method.
Since we are going to be telling Spring Security about our customization via a standard Spring Configuration, these classes will be able to have our Services injected into it. And we are going to have our UserDetailsService delegate the data loading to our CustomerService which calls our CustomerHibernateRepository.
Since Customer already implements UserDetails we can just return it. Now underneath the covers in our service's call to the Repository our query is something like this. This is using HQL, but I think you can easily see what basic SQL query will be derived from it.
One thing to note, for some reason, Spring Security wants all role names to begin with “ROLE_” otherwise it won't work. If you don't have ROLE_ in your database, then you want to make sure that any query you write also will prepend your roles with “ROLE_”. Nice little gotcha there.
Now that we have our two customized classes written, it is now time to tell Spring that we want to use them instead of the built in classes that Spring Security provides. This, of course, is a spring configuration xml file, which you should already have, if you are using Spring Security, regardless of customizing it. So for sake of brevity, I will not go into detail on every security tag.
First our configuration:
In our configuration we just create a bean for our CustomerUserDetailsService passing in a CustomerService dependency via a constructor-arg, then using the security namespace we just tell the AuthenticationManager that we want to use our UserDetailsService which will return our CustomerUserDetails object.
There we have, a fully customized UserDetails and UserDetailsService in Spring Security. Everything else from Spring Security is being used as normal. Because Spring Security allows us to change any little part, we can choose exactly what we want to customize without have to write the entire kitchen sink along with it. Good Luck and happy customizing!
Mark Spritzler is the owner of Perfect World Programming, LLC. Mark is a trainer and consultant for companies like SpringSource, NTier Training and JBoss. Mark is an expert in Spring, Hibernate and iPhone development. You can contact him through the "contact us" page at perfectworldprogramming.com.
Books by Spring's Founder, Rod Johnson