Tuesday, July 2, 2013

Role based Access control using Spring Security and MVC, Mapping LDAP Groups to Authorities for Authorization

Authentication and Authorization is integral part of any Java enterprise or web application. Since most of the company uses LDAP Active directory for authentication, authorization and Role based access control (RBAC), it's good to know How to implement Role based access control using Spring MVC and Spring Security. This is the second part of my articles on using Spring Security for authentication and authorization in Spring MVC based Java application. In last part, we have learned about doing LDAP authentication against Windows active directory, and in this Spring Security tutorial, we will learn How to map LDAP groups to authorities for implementing Role based access control or authorization. If you are developing an application, whose access is controled by adding user to a particular LDAP group, then you need a mechanism to load those LDAP group after successful authentication. Spring Secuirty uses GrantedAuthority class for holding all roles for a particular user. Based upon these roles, a particular user can perform certain functionality in your application. For example, a read only user can only see data, but a user with ADMIN role, can add or remove data from your application. After implementing Role based access control, you are free of user management task, those will be taken care by respective team which manages LDAP groups and access, usually Windows support teams. In this article, we will all the steps required to map LDAP groups to granted authorities in Spring Security. If you love to read books, than you may want to check Spring Security 3.1 By Robert Winch,Peter Mularien, a great book, which teaches all good features of Spring security including LDAP authentication and authorization in great details. If you are developing secure enterprise application in Java and considering spring security, this is the one of the best and must read book on Spring Security.

  

Steps to Map LDAP groups to Authorities for Role based Access Control (RBAC)

Mapping LDAP authorities into role based access Java spring security example1) Create an Application specific Authority classes, usually an enum with values like APP_USER, APP_ADMIN

2) Create Authority Mapper which will Map LDAP groups to application specific authority for example if group in LDAP is "Application Access (Gn)" than mapping that to APP_USER.

3) If you are authenticating against Active directory than provide your custom Authority mapper to ActiveDirectoryLdapAuthenticationProvider. After successful authentication, it will load all the groups for which authenticated user_id is member of, and map with application specific authority.

4) Use application specific authorities or roles as APP_USER or APP_ADMIN to secure your URL's by using
<intercept-url pattern="/secure/admin/**" access="hasRole('APP_ADMIN')"/>
 
<intercept-url pattern="/secure/user/**" access="hasRole('APP_USER')"/>
<intercept-url pattern="/secure/**" access="isAuthenticated()" />


Java code for Mapping LDAP Groups to Authorities using Spring Security

Here is the Java code, required to map LDAP groups into granted authorities of Spring Security. We need one class, usually enum to create roles supported by our application, this must implement GrantedAuthority interface, which is used to represent role in Spring Security. Now we need a Mapper class to map LDAP groups into granted authorities, this class must implement GrantedAuthoritiesMapper interface. We create instance of this class using Spring and provide names of LDAP groups for mapping with a particular role. For example, if application has two  roles USER and ADMIN and LDAP group "Application User Access (Gn)" is for User and "Application Admin Access (Gn)" is for Admin, then this information is configured in Spring configuration file and this authority mapper is provided to LDAP authentication provider. Keeping application role separate from LDAP groups allows you to cope up with any change in LDAP group name, you just need to change your spring configuration file.

LDAPGrantedAuthoritiesMapper.java
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
 
/**
 * LDAP Authorities mapper, Maps LDAP groups to APP_USER and APP_ADMIN
 */
public class LDAPGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper {
    private final String APP_USER ="Ldap User Group";   //default user ldap group
    private final String APP_ADMIN ="Ldap Admin Group"; //default adming ldap group
 
    public ADGrantedAuthoritiesMapper(String userGroup, String adminGroup) {
        APP_USER = userGroup;
        APP_ADMIN = adminGroup;
 
    }
 
    public Collection  mapAuthorities(
            final Collection authorities) {
 
        Set roles = EnumSet.noneOf(LDAPAuthority.class); //empty EnumSet
 
        for (GrantedAuthority authority : authorities) {
            if (APP_USER.equals(authority.getAuthority())) {
                roles.add(LDAPAuthority.APP_USER);
            } else if (APP_ADMIN.equals(authority.getAuthority())) {
                roles.add(LDAPAuthority.APP_ADMIN);
            }
        }
        return roles;
    }
}

LDAPAuthority.java
import org.springframework.security.core.GrantedAuthority;
 
/**
 * Maps LDAP Group application roles
 */
public enum LDAPAuthority implements GrantedAuthority{
    APP_USER, APP_ADMIN; //roles used in application
   
    public String getAuthority() {
        return name();
    }
   
}

Spring Security Configuration for Role based Access and Mapping LDAP groups

As stated above, first configuration is creating an instance of LDAPGrantedAuthoritiesMapper and mapping LDAP groups to application roles, so that when a user is successfully authenticated and comes with all LDAP groups, he is member of, those groups are read and converted into corresponding roles. Second configuration is to provide this mapper to ActiveDirectoryLdapAuthenticationProvider, this is similar to our last example of LDAP authentication, except <beans:property name="authoritiesMapper" ref="ldapAuthoritiesMapper"/>, which is requite to map LDAP groups to granted authorities for role based access control.

<beans:bean id="ldapAuthoritiesMapper" class="com.abc.web.security.LDAPGrantedAuthoritiesMapper">
        <beans:constructor-arg value="Ldap User Group" />
        <beans:constructor-arg value="Ldap Admin Group" />
</beans:bean>   
 
<beans:bean id="LdapAuthProvider"  class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
        <beans:constructor-arg ref="domain" />
        <beans:constructor-arg ref="url" />
        <beans:property name="convertSubErrorCodesToExceptions" value="true"/>
        <beans:property name="authoritiesMapper" ref="ldapAuthoritiesMapper"/>   //LDAP authority mapper
        <beans:property name="useAuthenticationRequestCredentials" value="true"/>
</beans:bean
 
That's all you need to implement Role based access control on your Spring MVC, Spring Security based Java web application. Like other features, LDAP authorization doesn't come out of box from Spring Security and you need to follow above steps to map LDAP groups to granted authorities.

Recommended Book:
Spring Security 3.1 By Robert Winch,Peter Mularien is one of the best and must read book on Spring security. It takes application development approach to teach basics of enterprise security, LDAP concepts, authentication, authorization and several other spring security features with non trivial examples.

1 comment :

Shawn McKinney said...

The name of this article should be changed to 'Using Roles for Access Control in Spring' rather than what it is currently called. Role-Based Access Control refers to ANSI RBAC INCITS 359 which uses roles in concert with permissions - and sessions. Binding roles directly to pages as you have done here is actually an anti-pattern for policy enforcement. The policy enforcement point should use always bind to permissions and let the access control mechanism determine what roles are needed.

Post a Comment