Integrated Open Souce Technology Solutions

Extending Acegi Security

April 9th, 2006 Posted in JAVA, Web

This article shows how to add several security enhancements to a web application that is utilizing Acegi Security. These security enhancements include:

  • How to add funcitonality to force the user to change thier password on thier first login
  • After a selected number of failed login attempts the system will temporarily lock the user account for a pre-determined amount of time
  • Not allow the user to re-use a pe-selected number of previous passwords that are stored in a password history.

Acegi Security Fundamentals

Acegi Security provides comprehensive security services for J2EE-based enterprise software applications. There is a particular emphasis on supporting projects built using The Spring Framework, which is the leading J2EE solution for enterprise software development. If you’re not using Spring for developing enterprise applications, we warmly encourage you to take a closer look at it. Some familiarity with Spring - and in particular dependency injection principles - will help you get up to speed with Acegi Security more easily.

To help understand this article I will explain some of Acegi security fundamentals.

SecurityContextHolder - This is where the details of the present security context of the application is stored, which includes details of the principal currently using the application. By default the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution, even if the security context is not explicitly passed around as an argument those methods.

Inside the SecurityContextHolder we store details of the principal currently interacting with the application. Acegi Security uses an Authentication object to represent this information. You won’t normally need to create an Authentication object yourself, it is fairly common for users to query the Authentication object.
You can use the following code block - from anywhere in your application - to do this:

Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (obj instanceof UserDetails) {

   String username = ((UserDetails)obj).getUsername();

} else {

   String username = obj.toString();

}

As the above code indicates the principal can be a username (String) or an object then implements the UserDeails interface. In our application we use the latter approach.

SecurityContext - Holds the Authentication and possibly request-specific security information.
HttpSessionContextIntegrationFilter - Stores the SecurityContext in the HttpSession between web requests.
Authentication - Represents the principal in an Acegi Security-specific manner.
GrantedAuthority - Reflects the application-wide permissions granted to a principal.
UserDetails - to provide the necessary information to build an Authentication object from your application’s DAOs.
UserDetailsService - Creates a UserDetails when passed in a String-based username (or certificate ID or alike).
Filters - Acegi Security uses many filters. You have a choice in how these filters are added to your web application, in that you can use either FilterToBeanProxy or FilterChainProxy. In my example application I use FilterChainProxy, which is configured through the web.xml as:

    <filter>
        <filter-name>securityFilter</filter-name>
        <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
        </init-param>
    </filter>

Notice that the filter in web.xml is actually a FilterToBeanProxy, and not the filter that will actually implement the logic of the filter. What FilterToBeanProxy does is delegate the Filter’s methods through to a bean which is obtained from the Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility. The bean must implement javax.servlet.Filter. The FilterToBeanProxy only requires a single initialization parameter, targetClass or targetBean. The targetClass parameter locates the first object in the application context of the specified class, whilst targetBean locates the object by bean name. Like standard Spring web applications, the FilterToBeanProxy accesses the application context via WebApplicationContextUtils.getWebApplicationContext(ServletContext), so you should configure a ContextLoaderListener in web.xml.In our sample application the FilterChainProxy is defined as follows:

    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
        <property name="filterInvocationDefinitionSource">
            <value>
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                PATTERN_TYPE_APACHE_ANT
                /remoting/**=httpSessionContextIntegrationFilterWithASCFalse,
			basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
		/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
			remoteUserFilter,anonymousProcessingFilter,concurrentSessionFilter,
			exceptionTranslationFilter,passwordChangeFilter,vendorSetupFilter,
			filterInvocationInterceptor
            </value>
        </property>
    </bean>

The above declarations will cause every web request to be passed through to Acegi Security’s FilterChainProxy. As explained in the filters section of the Acegi reference guide, the FilterChainProxy is a generally-useful class that enables web requests to be passed to different filters based on the URL patterns. Those delegated filters are managed inside the application context, so they can benefit from dependency injection.We are now ready to discuss the the security enhancements listed in the introduction.

Force the user to change password on first login

This requirement ensures that the user changes the password after the account is created. But this functionality can be expanded to force the user to change their password at specified intervals.

DaoAuthenticationProvider returns an Authentication object which in turn has its principal property set. The principal will be either a String (which is essentially the username) or a UserDetails object (which was looked up from the UserDetailsService). By default the UserDetails is returned, as this enables applications to add extra properties potentially of use in applications, such as the user’s full name, email address etc. If using container adapters, or if your applications were written to operate with Strings (as was the case for releases prior to Acegi Security 0.6), you should set the DaoAuthenticationProvider.forcePrincipalAsString
property to true in your application context.

In our sample application we are returning a user object that implements the UserDetails interface. We added the boolean property passwordChangeRequired to the user object to indicate if a password change is required.

The next step will be to add a filter that will direct the user to the change password page. This will be handled by adding a changePasswordFilter that intercepts all requests. This filter will redirect the request to the changePassword jsp page if the passwordChangeRequired flag is set on the user object.

The diagram below displays a simple login flow:

The passwordChangeFilter will have to be robust enough to avoid recursion and avoid redirect if the logout page is being displayed.

The setting of the User.passwordChangeRequired property can be defaulted to true so that all users added to the system will be required to have their password changed. This works great if an admin is adding users to the system but may not be desired if the users are registering themselves. Clearng of the User.passwordChangeRequired property is handled thorugh the changePasswordController which is a standard Model-View-Controller (MVC) that handles setting the user password and clearing this User.passwordChangeRequired property.

Temporarily lock user account after failed login attempts

This requirement is usefull at discouraging attempts at hacking the user password using automatted bots. If the user account is lociked for 30 minutes after three invalid login attempts this would presumably stop any attempt at trying to automatically guess the password, it would take to long.

  1. 2 Trackback(s)

  2. May 3, 2010: Kylie Batt
  3. Jul 6, 2010: MITCHELL

You must be logged in to post a comment.