Almost by its very nature, one of the most tedious and difficult aspects of application development is security, specifically authentication and authorization. Most multi-user applications need to confirm that a user is whom he says and then has appropriate authorized access to the necessary resources. Therefore, security is often one of the most important aspects. The collision of these factors has the impact of making security forgetful, error prone, and potentially dangerous, especially for enterprise applications.
While the J2EE specification and JAAS, the Java Authorization and Authentication Service, provide a step in the right direction, they are far from complete. Every application server vendor is free to implement container security differently nor are they required to use JAAS. This leads to portability and user management constraints. Furthermore, it still does not approach security in the manner as described above- as an aspect.
Enter the Acegi Security framework, an open source security framework designed for Spring. Created by Ben Alex, the framework has begun to gather a loyal following for its comprehensive list of features, excellent unit test coverage, ease of use, and loosely coupled integration with Spring. While the framework was purposely designed for Spring, there is no reason it could not be used with non-Spring applications, especially web applications.
An introduction to Acegi, its core components, and configuration via Spring’s application context. With this knowledge, we will learn how to implement authentication and authorization services for a simple web application.
Before deciding to grant or deny access to a resource, the user must provide the appropriate security identification. For this reason, Acegi provides two key interfaces for providing authentication services — Authentication and AuthenticationManager. Let’s examine each of these to find out how they form a complete authentication system.
The Authentication interface which holds three important objects. The first object is the principal, which identifies the caller (user). The second object are the credentials that provide proof of the identity of the caller. For traditional logins, this is the username’s respective password. The final object contained by the Authentication interface is and array of the authorities granted to the principal.
During the authentication process, an implementation of the Authentication interface is populated with the principal and credentials by client code. For example, a web application presents the user with a prompt for username and password. The supplied username and password are then used to create the Authentication object. Yet, that still leaves one of the three contained objects empty, the array of granted authorities. Here is where the AuthenticationManager plays its role in the authentication chain.
AuthenticationManager specifies a single method, authenticate.
public Authentication authenticate(Authentication authentication) throws AuthenticationException;
This method takes the two-thirds populated Authentication object as a parameter. From there, the method has two options, either return a fully populated Authentication object or throw an AuthenticationException. If the correct principal and credentials were provided, theAuthenticationManager does the former by returning a fully populated Authentication object. It supplements it by populating the authorities granted to the authenticated principal. Should authentication fail, an AuthenticationException is thrown that represents the reason why via a number of subclasses.
The most important subclasses are BadCredentialsException, DisabledException, and LockedException. As one would imagine, the first is thrown when an incorrect principal and credentials are provided. The other two are thrown when the principal account is either disabled or locked out, respectively. Therefore, the credentials were not checked and authorization has been denied.
While the above interfaces are important, especially to developers creating custom authentication mechanisms for Acegi, the primary value for most is an understanding of the authentication chain. Most developers should consider using one of the provider-based authentication packages included. The most popular implementation of the AuthenticationManagers is the ProviderManager.
For all practical purposes, the ProviderManager is nothing more than a wrapper around a list of one or more AuthenticationProvidersprovided to the class. During authentication, the wrapper class cycles through the list of AuthenticationProviders until a compatible provider is located. Once located, the authenticate method of the AuthenticationManager delegates to that specific provider. In response, the provider either returns the fully populated Authentication object or throws an AuthenticationException. As expected, the cumulative result of all providers is returned from the wrapper (ProviderManager).
Several AuthenticationProviders implementations are provided with the Acegi Security System, including those for JAAS and JBoss authentication. For this article, we will be using the DaoAuthenticationProvider, which is able to authenticate a username and password combination against a repository, such as a database or in-memory hash. This provider is easy to understand, configure, and demonstrate. However, readers should examine the other providers to determine the one that suits their needs best.
With each level of abstraction and delegation comes flexibility. In the case of the DaoAuthenticationProvider, it delegates to an implementation of the AuthenticationDao interface, which has a single method, loadUserByUsername. This method that takes a username and loads the respective user details to verify for authentication by InMemoryDaoImpl Developers are free to create their own implementation, for example, using Hibernate; however, Acegi ships with two very usefully implementations, a JDBC-based and memory-based. For simplicity, this article will use the latter, InMemoryDaoImpl.
Enough with the explanation and abstraction, let’s begin by configuring the aforementioned components starting with theAuthenticationDao. (This article assumes the user is vaguely familiar with Spring XML configuration.) The configuration below creates an instance of the InMemoryDaoImpl with a user named “matthew”. This user has a password of “contegix” and a granted authority of “ROLE_ADMIN”.
<bean id="memoryAuthenticationDao"> <property name="userMap"> <value> matthew=contegix,ROLE_ADMIN </value> </property> </bean>
Excellent. The next step is to create an instance of the InMemoryDaoImpl with the memory authentication DAO listed as a provider.
<bean id="daoAuthenticationProvider"> <property name="authenticationDao"> <ref local="memoryAuthenticationDao"/> </property> </bean>
Finally, let’s take the next step up and create the authentication managers with the DAO authentication provider as the sole provider.
<bean id="authenticationManager"> <property name="providers"> <list> <ref bean="daoAuthenticationProvider"/> </list> </property> </bean>
At this point, the authentication manager is fully configured and ready for use. The next step is to tie this into our fictional web application.
There are a number of ways to perform authentication for web applications. The most prevalent are Basic Authentication as defined by RFC 1945, Section 11 and pure HTTP Session Authentication. For our fictional application, we will use the latter.
Acegi performs HTTP session authentication through the use of a servlet filter. The filter, AuthenticationProcessingFilter, processes information from a programmed login form containing two parameters — j_username and j_password. One unique aspect of the Acegi authentication filter is the fact that it delegates to a bean proxy defined in Spring’s application context using FilterToBeanProxy. Therefore, all Acegi filters will be configured using as an instance of this class and must be provided either a targetClass or targetBean via an initialization parameter that points to the bean in the application context. The application context bean is configured with the parameters for authentication rather than the filter.
<filter> <filter-name>Acegi Authentication Processing Filter</filter-name> <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class> <init-param> <param-name>targetClass</param-name> <param-value> net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter </param-value> </init-param> </filter> <filter-mapping> <filter-name>Acegi Authentication Processing Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<bean id="authenticationProcessingFilter"> <property name="authenticationManager"> <ref bean="authenticationManager"/> </property> <property name="authenticationFailureUrl"> <value>/login.jsp?error=1</value> </property> <property name="defaultTargetUrl"> <value>/</value> </property> <property name="filterProcessesUrl"> <value>/j_acegi_security_check</value> </property> </bean>
Let’s look at the properties passed in the AuthenticationProcessingFilter bean. The first property is relatively self-explanatory. It is a reference to the configured authentication manager. Obviously, the bean will utilize this to proceed through the authentication chain.
If authentication fails, the browser will automatically be redirected to the URL specified by authenticationFailureUrl. If authentication is successful, the browser will be redirected to the protected URL that forced the authentication. This allows the user to be automatically returned to what he was trying to access. If no resource was specified, for example when the user directly accesses the login URL, thedefaultTargetUrl property specifies where the user will be redirected.
Finally, the filterProcessesUrl is the URL by which this filter will be activated. For the developer, this URL should be specified as the action parameter in the HTML form.
Now that our fictional application can authenticate users, let’s begin placing access constraints on resources.
The concept of Security Interception is key to protecting resources under Acegi. Prior to access to the resource, interception determines whether or not the resource should be protected. If so, interception examines who made the call (the principal) and whether or not access should be granted. Based upon the result, the interceptor either allows the request or not. Let’s examine in-depth how this process occurs.
Tracing the chain of authorization, the security interceptor receives access to a protected resource. Assuming the user is authenticated, it delegates to an implementation of the AccessDecisionManager, which receives key parameters such as the authenticated Authentication object and resource properties, among others. The final decision for access is left in the hands of theAccessDecisionManager.
While developers are welcome to implement a custom AccessDecisionManager when appropriate, most circumstances allow for use of the implementations that are based upon the concept of voting. Acegi currently ships with three implementations of AccessDecisionManagerthat tally votes — ConsensusBased, UnanimousBased, and AffirmativeBased. The ConsensusBased implementation grants or denies access based upon the consensus of non-abstain votes. As the name suggests, the UnanimousBased implementation requires unanimous consent in order to grant access but does ignore abstains. Finally, the AffirmativeBased implementation grants access if at least one access granted is received while deny votes are disregarded.
The question that should come to mind is how does a voting AccessDecisionManager determine which way to cast a vote. Here is whereAccessDecisionVoters play a role in the authorization decision chain. The sole shipping implementation of this interface is the RoleVoter, which grants access if the principal has been assigned the role. Role assignments are the elements of its granted authority array of the respective authenticated Authentication object. In other words, if a resource requires a role of SUPERVISOR, the user will be granted access if it is listed as one of its granted authorities when he authenticated.
For the case of our fictional application, the UnanimousBased implementation with the RoleVoter will suffice for our minimalist needs; however, it is truly important to understand the complex structure and role of AccessDecisionManagers in Acegi. Please consult the reference documentation to learn more.
Let’s configure the authorization system by crawling back up the chain, starting with the RoleVoter and UnanimousBased.
<bean id="roleVoter"/> <bean id="accessDecisionManager"> <property name="allowIfAllAbstainDecisions"> <value>false</value> </property> <property name="decisionVoters"> <list> <ref local="roleVoter"/> </list> </property> </bean>
The RoleVoter exists as a simple bean instantiation with no properties. The UnanimousBased has two properties configured. The first one determines whether or not access should be granted if all AccessDecisionVoters abstain. The second is a reference to the instantiatedRoleVoter.
The next step is to configure the security interception system. In the case of web applications, security interception is done using a servlet filter – SecurityEnforcementFilter — in combination with the FilterSecurityInterceptor. These two objects work in conjunction to provide authorization access decisions for URL-based resource. For the most part, the filter handles session management and URL redirection for user login (as specified by an AuthenticationEntryPoint object) while delegating to the interceptor for security decisions.
As before, the filter utilizes the FilterToBeanProxy class to retrieve an instantiated bean from the application context.
<filter> <filter-name>Acegi HTTP Request Security Filter</filter-name>> <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class> <init-param> <param-name>targetClass</param-name> <param-value> net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter </param-value> </init-param> </filter> <filter-mapping> <filter-name>Acegi HTTP Request Security Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<bean id="securityEnforcementFilter"> <property name="filterSecurityInterceptor"> <ref bean="filterInvocationInterceptor"/> </property> <property name="authenticationEntryPoint"> <ref bean="authenticationEntryPoint"/> </property> </bean> <bean id="authenticationEntryPoint"> <property name="loginFormUrl"> <value>/login.jsp</value> </property> </bean> <bean id="filterInvocationInterceptor"> <property name="authenticationManager"> <ref bean="authenticationManager"/></property> <property name="accessDecisionManager"> <ref bean="accessDecisionManager"/></property> <property name="objectDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /secure/super/**=ROLE_SUPERVISOR /secure/.* =ROLE_USER,ROLE_SUPERVISOR </value> </property> </bean>
Starting from the bottom, a FilterSecurityInterceptor is declared and passed both the authentication manager and the access decision manager. Furthermore, the object receives a set ObjectDefintionSources. Each value provides specific meanings. The first value informs the interceptor to convert all URLs to lowercase before evaluating. PATTERN_TYPE_APACHE_ANT is an instruction detailing the format of the final two values. It tells the interceptor to examine the remaining parameters using Apache Ant style pattern matching rather than the default pattern matching using regex. (This is a personal preference of the author and is not required.)
The final two values are URL patterns to secure. The left-hand side of the equals is the URL pattern while the right-hand side details the roles necessary for casting a grant vote. In this example, anything under /secure/super/ will only be accessible by principals who have aSUPERVISOR role while /secure/ will be accessible to principals with either the USER or SUPERVISOR role.
The AuthenticationEntryPoint will be called if a user requests a protected URL when they are not authenticated. Finally, the targetClassbean is declared and passed the reference to the AuthenticationEntryPoint and the FilterSecurityInterceptor.
Voila! Our example application now has everything it needs to protect at least two URL resources based upon roles and perform authentication.
Acegi is one the best security framework available for the Java platform. Even though the configuration utilizes Spring, this article demonstrate the power of the system while showing there is no reason why it can not be used even when not integrating Spring into your application. Furthermore, the entire framework serves as an excellent example of extensibility through abstraction