Acegi Security in one hour

A concise guide to securing your Java Web applications

1 2 3 4 5 6 7 Page 6
Page 6 of 7

Customizing Acegi Security for dynamic authorization

The implementation example you've just completed is based on the assumption that authorization details won't change in the application's lifetime. Accordingly, in the filterInvocationInterceptor bean definition in Listing 11, the objectDefinitionSource property contains the actual URL-to-role mapping. But for some applications, authorization access needs to be dynamic. For instance, one particular URL might start out being accessible only to a certain role. But over time, business requirements might dictate that you change or add roles against that URL. For those cases, it's better to have URL-to-role mapping in a database instead of a static configuration file.

To make authorization details dynamic, the first task is to change the implementation of the objectDefinitionSource property to get URL-to-role mapping from a database. FilterSecurityInterceptor uses an ObjectDefinitionSource implementation specific for Web applications. (It implements FilterInvocationDefinitionSource interface, which is derived from the ObjectDefinitionSource interface.) To implement a new custom FilterInvocationDefinitionSource, you need to implement its three methods.

In the Acegi Security framework, you already have a class named AbstractFilterInvocationDefinitionSource, which implements two methods but adds a new abstract method. So you are left with implementing two methods (getConfigAttributeDefinitions() and lookupAttributes()) in total in your custom FilterInvocationDefinitionSource implementation. Because the getConfigAttributeDefinitions() method is used by AbstractSecurityInterceptor to help with initial configuration checking, you can return null from this method. The only method left to implement is lookupAttributes(). Listing 20 shows a sample implementation of the lookupAttributes() method.

Listing 20. Implementation of the lookupAttributes() method

public ConfigAttributeDefinition lookupAttributes(String url) {

    // Strip anything after a question mark symbol, as per SEC-161. See also
    // SEC-321
    int firstQuestionMarkIndex = url.indexOf("?");

    if (firstQuestionMarkIndex != -1) {
      url = url.substring(0, firstQuestionMarkIndex);
    }
    SecureResource secureObject = authorizationService.getSecureObject(url);
    if (secureObject == null)// if secure object not exist in database
      return null;
    // retrieving roles associated with this secure object
    List<Role> secureObjectRoles = authorizationService
        .getSecureObjectRoles(secureObject);
    // creating ConfigAttributeDefinition
    if (secureObjectRoles != null && !secureObjectRoles.isEmpty()) {
      ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
      StringBuffer rolesStr = new StringBuffer();
      for (int i = 0; i < secureObjectRoles.size(); i++) {
        Role sor = (Role) secureObjectRoles.get(i);
        rolesStr.append(sor.getName()).append(",");
      }
      configAttrEditor.setAsText(rolesStr.toString().substring(0,
          rolesStr.length() - 1));
      ConfigAttributeDefinition configAttrDef = (ConfigAttributeDefinition) configAttrEditor
          .getValue();
      return configAttrDef;
    }
    return null;

  }

In Listing 20, you use AuthorizationService to get the mappings between a secured resource and its associated roles. This sample uses AuthorizationServiceImpl to interact with database layer. For the sake of simplicity, the sample application uses the in in-memory data store instead of a database for the data-access object (DAO) implementation. You can change its implementation using Java Database Connectivity (JDBC), the Java Persistence API (JPA), or any other persistence mechanism you like.

Now that you're done implementing the custom FilterInvocationDefinitionSource, you need to configure it in the Acegi configuration. Listing 21 shows the modified filterInvocationInterceptor bean definition.

Listing 21. Modified filterInvocationInterceptor bean definition

<bean id="filterInvocationInterceptor"
    class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">

    <property name="authenticationManager">
      <ref bean="authenticationManager" />
    </property>
    <property name="accessDecisionManager">
      <ref local="httpRequestAccessDecisionManager" />
    </property>

    <property name="objectDefinitionSource">
      <ref local="dbdrivenFilterInvocationDefinitionSource" />
    </property>
  </bean> 
  <bean id="dbdrivenFilterInvocationDefinitionSource"
    class="com.abc.security.authorization.DatabaseDrivenFilterInvocationDefinitionSource">
    <property name="authorizationService">

      <ref local="authorizationService" />
    </property>
  </bean>

  <bean id="authorizationService"
    class="com.abc.security.authorization.service.AuthorizationServiceImpl">
    <property name="authDAO">
      <ref local="authDAO" />

    </property>
  </bean>
  <bean id="authDAO"
    class="com.abc.security.authorization.dao.AuthorizationDAOImpl" />

Now this interceptor will hit the database for all authorization information. You might want to cache this information to minimize the performance impact of repeated hits to the database. You can cache it in the DAO layer using features in object-relational mapping (ORM) persistence implementations such as Hibernate, or you can cache it on the service layer.

1 2 3 4 5 6 7 Page 6
Page 6 of 7