Acegi Security has been generating some serious positive buzz among Java enterprise developers, so you might be wondering how it works. In this article, ShriKant Vashishtha walks you through all the steps of a hands-on Acegi Security implementation. First you'll set up form-based authentication and authorization services for a Java-based Web application, then you'll customize Acegi Security for dynamic authorization, as well as integration with proprietary authentication implementations such as LDAP.
Acegi Security is a powerful and flexible security solution for Java enterprise applications built using the Spring framework. Spring-based dependency injection makes Acegi easy to configure and implement in a completely nonintrusive way. This is a boon to organizations that might not want to implement the Spring framework as a whole but still need effective, reusable security for legacy applications.
This article gives you a concise jump-start to implementing Acegi Security for a basic order-processing application. You'll set up authentication and authorization services for the application, and you'll implement those security features in form-based Web pages. After working through the example, you should be able to set up basic form-based security for any Web application in about an hour.
Following a quick introduction to the implementation example, you'll learn about some of the ways you can customize application security using Acegi. You'll see how to set up dynamic role-based authorization based on a database that maps user roles to URLs. Finally, you'll find out how to create a custom Acegi Security authentication implementation that can integrate with existing proprietary authentication implementations.
I wanted to demonstrate Acegi's applicability to a wide range of implementations, not just Spring-based applications. I built the example application using JEE 5, with JavaServer Pages for the presentation layer and SiteMesh for Web layout. The application could just as easily be built using Struts 2, and the Struts 2 infrastructure is already in place in the source code, though not implemented. I used Spring dependency injection to implement Acegi security for the application. See the Resources section to download the application source code. Follow these steps to set up the application environment:
Step 2. Create the following folder structure in a Java project:
src- Contains Java source code
test- Contains test cases
config- Any property/XML configuration file that needs to be inside the classpath
web- Contains the Web application
decorators- Contains SiteMesh decorators
images- Contains images, if any
styles- Cascading Style Sheets (CSS)
jsp- Contains JavaServer Pages files (JSPs)
lib- Contains JARs
Step 3. Copy the following JAR files into the WEB-INF/lib directory:
acegi-security-1.0.5.jar- Main classes of the Acegi Security system
cglib-2.1.3.jar- Code-generation library used by Spring
commons-codec-1.3.jar- Encoders and decoders such as Base64, Hex, Phonetic, and URLs
commons-lang-2.1.jar- Helper utilities for
ehcache-1.2.3.jar- Used for basic caching purposes
freemarker-2.3.8.jar- Used by the Struts implementation
jstl.jar, standard.jar- JavaServer Pages Standard Tag Library (JSTL) tag library
log4j-1.2.13.jar- For logging
ognl-2.6.11.jar- OGNL library used by the Struts implementation
sitemesh-2.3.jar- SiteMesh JAR
spring.jar- Spring Framework JAR
struts2-core-2.0.8.jar- Struts 2 core JAR
xwork-2.0.3.jar- Used by Struts
Changes to web.xml
Because Acegi Security is based on the concept of servlet filters and interceptors, you need to add entries for the
FilterToBeanProxy filter to your application's
web.xml deployment descriptor, as shown in Listing 1.
Listing 1. Adding servlet filters to web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <display-name>AcegiTraining</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext*.xml</param-value> </context-param> <filter> <filter-name>Acegi Filter Chain Proxy</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> ... ... <filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>/j_acegi_security_check</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>/j_acegi_logout</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> ... </web-app>
FilterToBeanProxy requires an initialization parameter,
targetClass parameter locates the first object of the specified class in the application context. In the configuration in Listing 1, that class is
org.acegisecurity.util.FilterChainProxy. The related bean object in the application context is
filterChainProxy, shown in Listing 2.
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value> ... </value> </property> </bean>
Notice that Listing 1 defines multiple filter mappings for the Acegi filter. You could instead get away with using a more general filter mapping, as shown in Listing 3.
Listing 3. A general filter mapping
<filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Order is essential when placing servlet filters. Because the example application uses filters for Acegi, JSP, and SiteMesh, you need to place the Acegi filter first, followed by the JSP and SiteMesh filters, respectively.