Spring Security customization (Part 1 – Customizing UserDetails or extending GrantedAuthority)

Oleg Zhurakousky

This is the first part of what I hope will become a multipart series of small posts showing practical examples around Spring Security customization. The requirements for these customizations are not imaginary and all came from the field. . .

Assume you have the following requirement. You have a list of roles where each role contains  list of business functions applicable to this role (see below):

ROLE_ADMIN
    BF_QUOTE_CREATE
    BF_POLICY_CREATE
    BF_POLICY_DELETE

ROLE_AGENT
    BF_QUOTE_CREATE
    BF_POLICY_CREATE

ROLE_USER
    BF_QUOTE_CREATE

The trick is to be able to make authorization decisions based on either.

For example:
User who has a role ROLE_ADMIN should be given access to any resource protected by this role.

<sec:authorize ifAllGranted="ROLE_ADMIN">
    <p><a href="http://www.google.com">Google</a>
</sec:authorize>

or

@Secured("ROLE_ADMIN")
public void foo()
    . . .
}

The same user should be given access to any resource protected by the corresponding business function.

<sec:authorize ifAllGranted="BF_POLICY_DELETE">
    <p><a href="http://www.google.com">Google</a>
</sec:authorize>

or

@Secured("BF_POLICY_DELETE")
public void foo()
    . . .
}

There are actually several ways for handling this requirement. One of them would be to create a RoleHierarchy and use RoleHierarchyVoter to traverse hierarchy of roles. The downside of this approach is that in the current implementation of Spring Security 2.0.4, taglibs (security: authorize . . . ) do not go through AccessDecisionManager to make a decision, thus no Voters are playing any role while making decisions about protecting HTML elements. However given the amazing flexibility and customization power of Spring Security, accomplishing this requirement is still quite simple.
One of the biggest benefits of Spring Security is customizations around how Principal (UserDetails object) is created. When UserDetails object is created it is populated with the list of GrantedAuthorities. This list is later inspected to match against GrantedAuthority protecting a resource.
One of the customization we can do is to customize the list of GrantedAuthorities during the creation of UserDetails object.

In the supplied example there are two property files (for simplification I am using property files, however you can easily modify it to use DB or LDAP).
One file users.properties maps user to roles

oleg=powder,ROLE_ADMIN

while the other one role-to-bf.properties maps roles to the list of business functions

ROLE_ADMIN=BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE

Our goal is to create a UserDetails object which contains a list of GrantedAuthorities representing both roles and business functions.
For example: for the user oleg the list of GrantedAuthorities should be:

ROLE_ADMIN,BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE

So all we need is define a custom implementation of UserDetailsService where by using both property files (could be DB or LDAP in real life) we will create custom list of GrantedAuthorities and then inject them into the final UserDetails object. This is quite simple and we can reuse the existing implementation of GrantedAuthority interface such as GrantedAuthorityImpl. However, we also want to make sure that we can trace (for debugging or any other purpose) the parent GrantedAuthority for each GrantedAuthority that represents a business function.
In order to accomplish both of these goals we will extend GrantedAuthorityImpl by defining a BusinessFunctionGrantedAuthority class which simply contains a list of all parent GrantedAuthority objects which define such business function.

public class BusinessFunctionGrantedAuthority extends GrantedAuthorityImpl {
    private List<GrantedAuthority> parentAuthorities;
        . . .
}

Then we will create a custom implementation of UserDetailsService and implement loadUserByName(..) method where we will perform the following:

1. Create UserAttribute object  based on contents of users.properties file. UserAttribute will contain the list of GrantedAuthorities representing roles.
2. Iterate through the list of role-GratedAuthorities and for each role-GrantedAuthority create a BusinessFunctionGrantedAuthority and add it to the overall list of already created GrantedAuthorities
    2.1 Add parent GrantedAuthority to each BusinessFunctionGrantedAuthority
3. Create final UserDetails object which contains the full list of GrantedAuthorities.

Then define your AuthenticationProvider in you Spring Security configuration:

NOTE: we are injecting AuthenticationProvider with custom UserDetailsService implemented by ComplexAuthorityUserDetailsService class.(see sample code for more details)
Secure your resources, deploy and access application: http://localhost:8080/spring-security-sample-grantedAuthority/index.jsp
After Logging on you should see the list of GrantedAuthorities being displayed along with other properties of the Principal:

You can clearly see that GrantedAuthority representing business function also shows the list of parent GratedAuthorities defining such business function.
Inspect index.jsp and observe how security:authorize tag is using both roles and business functions to protect HTML elements
That is all. You can clearly see how with few minor customizations you can easily extend and customize the structure of the Principal and apply declarative protection based on custom GrantedAuthorities without polluting your business code with custom security code.

Sample code could be downloaded here: spring-security-sample-grantedauthority

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • TwitThis
 

14 responses


  1. Thanks for the interesting article!

    Will try to implement spring security in my webapp next week so might be quite useful.

    Greets,

    Jochen


  2. Glad to hear that

    Oleg


  3. Nice article
    But it would have been great if u would explained the basic flow of Spring security… instead directly going to UserDetailsService.

    Might be start with SecurityContextHolder -> SecurityContext -> Authentication -> AuthenticationManager -> Provider -> Service & then UserDetails

    Thanks,
    Senthil


  4. Although this post assumed that reader has some basic understanding of Spring Security, I hear your point :-)
    I'll try to find some time to do it. . .
    Do you have any specific problem you are experiencing?


  5. Thanks. No, as of now… I don't have any problem. It would be really good for others if it starts from scratch.

    -Senthil


  6. Hello,

    Thanks for the article. Would appreciate if you could provide an example with LDAP. We are in the process of implementing a solution for our application.

    Thanks


  7. Hi,

    Thanks for both articles. Would appreciate if you provide an example using database tables instead properties files.

    Thanks!


  8. Could you please help me with with configuring my IDE so that I can use 2.5 MVC framework and Security. I'm using NetBnz 6.5. I have the 2.5 framework, but it doesn't have the security .jars included. I downloaded the security .zip file, but I don't know what to do with it. Can I even use NetBnz to implement my apps? I would really appreciate it if you could help me out. Maybe I need to use a different IDE? Thank you very much.


  9. Why I'm not able to do following


  10. intercept-url pattern="/index.htm*" access="ROLE_ADMIN" /


  11. I have little different scenario.
    – In my the case password stored in DB is in encrypted form.
    – I have implemented 'UserDetailsService' so I need to give the implementation of loadUserByUsername(String username) method. But in this method, we get only the username that user has entered on login page. How can I get the password?

    If somehow I get the password then I will encrypt the password and pass it to User (org.springframework.security.userdetails.User) Object.


  12. Great article Thanks very much. You saved me a lot of time.

    I've to do a little modify to make it working… Spring in security tags take account of only strings that start with "ROLE" and then our Businness functions that starts with "BF" are ignored.

    To avoid this I've set rolePrefix to "".

    That's the code:

    Giovanni


  13. i hope you can help me with my problem or point me to where i can. im new to implementing spring and spring security

    our security requirement goes like this. once a user logs into the application we will do a check to see if he has more than 1 role. if the user has more than 1 role we will show a page of the roles that the user has and ask the user to select the role that he wanted for that session.

    i already created a subclass of the userdetails object and when the user selected the role i will modify the user object setAuthorities.

    when doing this:
    User user = (User) authentication.getPrincipal();
    GrantedAuthority[] grarray=user.getAuthorities();

    it is returning the updated Authorities but when doing this on the JSP page:

    the "remove role" role not selected is still being seen which im expecthing should not since it is not in the userdetails anymore…

    i hope you could help me. thanks a lot in advance.


  14. Can any one explain about "SwitchUser feature of Spring Security" in Spring3.0.x ?
    Can you suggest me any online tutorials about "SwitchUser feature of Spring Security" in Spring3.0.x ?

    Thanks In Advance

    Thanks && Regards
    Venu.K

One trackback

Leave a Reply