Spring Security: Customizing the User Authorization Process

Spring framework

Spring Security: Customizing the User Authorization Process

By Mark Spritzler

TheServerSide.com

Mark is a trainer and consultant for companies like SpringSource, NTier Training and JBoss. Mark is an expert in Spring, Hibernate and iPhone development.

Customizing your user and authorization in Spring Security

 

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.

In Spring Securirty a User/Principal is an instance of the UserDetails interface.

 

public interface UserDetails extends Serializable {

      GrantedAuthority[] getAuthorities();

      String getPassword();

      String getUsername();

      boolean isAccountNonExpired();

      boolean isAccountNonLocked();

      boolean isCredentialsNonExpired();

      boolean isEnabled();

}



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.

 

public class Customer implements UserDetails {

    private String name;
    private Address address;
    //..... getters and setters for the top two props   

    // Spring Security related methods and instance Vars

    private String userName;
    private String password;
    private GrantedAuthority authorities;

    GrantedAuthority[] getAuthorities() {  return authorities;}

    String getPassword() {  return this.password; }

    String getUsername() {  return this.userName; }

    boolean isAccountNonExpired() { return false;}

    boolean isAccountNonLocked() { return false; }

    boolean isCredentialsNonExpired() {return false; }

    boolean isEnabled() {
        // We don't have an enabled/active field in our DB, all users are always enabled.
        return false;
    }

 

    public void setCustomerAuthorities(List<String> roles) {
        List<GrantedAuthority> listOfAuthorities = new ArrayList<GrantedAuthority>();
        for (String role : roles) {
            listOfAuthorities.add(new GrantedAuthorityImpl(role));
        }
        authorities = (GrantedAuthority[])ListOfAuthorities.toArray();
    }

}

 

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.


public interface UserDetailsService {

     UserDetails loadUserByUsername(String username)
                    throws UsernameNotFoundException, DataAccessException;

}

 

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.

 

 

public class CustomerUserDetailsService implements UserDetailsService {

    private CustomerService customerService;

    public CustomerUserDetailsService(CustomerService customerService) {

        this.customerService = customerService;

    }

    public UserDetails loadUserByUsername(String userName) {

        Customer customer = customerService.getCustomerByUserName(userName);

        List<String> roles = customerService.getRolesByUserName(userName);

        customer.setCustomerAuthorities(roles);

        return customer;         

    }

}

 

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.

 

“FROM Customer c where c.userName = :userName”

and

“Select role FROM Role r WHERE r.userName = :userName”

 

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:

 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                                      http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/security
                       http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">

    <security:http>
          <intercept-url go here>
    </security:http>

    <!-- Temporary Login from Database -->
    <security:authentication-provider user-service-ref="customerUserDetailsService"/>

    <bean id="customerUserDetailsService" class="com.severside.CustomerUserDetailsService">
        <constructor-arg ref=”customerService”/>   
    <bean>

</beans>

 

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.

www.perfectworldprogramming.com
mediaserverblog.wordpress.com


Read Mark Spritzler's column on Spring Formatters and Converters

***********************

Books by Spring's Founder, Rod Johnson

 

26 Apr 2010

Disclaimer: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.