For this example, I use two secure JSPs, the secure/authenticatedusers.jsp file is available to all the authenticated users, and secure/admin/admin.jsp is only available to the users with ADMIN role. The login.jsp is used for login and denied.jsp is used to redirect unauthorized users. Follow these steps to run the example:
- Download Acegi and Spring.
- Start with a dynamic web project in eclipse.
- Copy the following Jar files into the WEB-INF/lib directory.
acegi-security-1.0.3.jar Main classes of the Acegi security system. commons-codec-1.3.jar Encoders and decoders such as Base64, Hex, Phonetic and URLs. ehcache-1.2.3.jar Used by the authentication provider. jstl.jar, standard.jar The JSTL tag library spring.jar Spring framework commons-logging.jar, cglib-nodep-2.1_3.jar Available in the spring download (spring with dependencies) - Create the Login page: The following is a listing for the login page.
<jsp:root version="1.2" xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="urn:jsptld:http://java.sun.com/jsp/jstl/core">
<jsp:directive.page contentType="text/html; charset=UTF-8" />
<jsp:directive.page
import="org.acegisecurity.ui.AbstractProcessingFilter, org.acegisecurity.ui.webapp.AuthenticationProcessingFilter, org.acegisecurity.AuthenticationException" />
<head>
<title>Login</title>
</head>
<body>
<form action="j_acegi_security_check" method="POST">
<table>
<tr>
<td>User:</td>
</td><input type='text' name='j_username' />
</td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='j_password' /></td>
</tr>
<tr>
<td><input type="checkbox" name="_acegi_security_remember_me" /></td>
<td>Remember me (14 days)</td>
</tr>
<tr><td colspan='2'><input name="submit" type="submit" /></td></tr>
<tr><td colspan='2'><input name="reset" type="reset" /></td></tr>
</table>
</form>
</body>
</jsp:root>login.jsp
Notes:- The login form uses j_acegi_security_check instead of j_security_check normally used for form-based authentication. Similarly for logout, we use j_acegi_logout action.
- In order to remember a user for a period of time, use the _acegi_security_remember_me attribute. This is configured in the applicatonContext.xml file(see below).
- Create the other JSP files: The listings for the JSP pages used for this example are shown below, along with their respective directories.
<html>
<body>
Everyone
<p><a href="secure/authenticatedusers.jsp">Authenticated users only</a>
<p><a href="secure/admin/admin.jsp">Admins only</a>
</body>
</html>index.jsp Access Denied.
denied.jsp <%@ page import="org.acegisecurity.context.SecurityContextHolder" %>
<h1>Welcome: <%= SecurityContextHolder.getContext().getAuthentication().getName() %></h1>
<p><a href="../">Home</a>
<p><a href="../j_acegi_logout">Logout</a>secure/authenticatedusers.jsp <%@ page import="org.acegisecurity.context.SecurityContextHolder" %>
<h1>Welcome: <%= SecurityContextHolder.getContext().getAuthentication().getName() %> is an Admin</h1>
<p><a href="../../">Home</a>
<p><a href="../../j_acegi_logout">Logout</a>
</body>
</html>secure/admin/admin.jsp - The users.properties file: This example uses an in memory user registry and the WEB-INF/users.properties file is used to store the usernames and passwords.
scott=tiger,ROLE_USER
harry=potter,ROLE_ADMIN
frodo=baggins,ROLE_USERWEB-INF/users.properties - Configure the Acegi filter in the Web deployment descriptor: The web.xml file is shown below
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
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">
<display-name>SpringSecurity</display-name>
<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>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>web.xml - Configure Acegi in applicationContext.xml file: The following is a listing of the WEB-INF/applicationContext.xml file, followed be explanations of the different beans defined in there.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/index.jsp"/>
<constructor-arg>
<list>
<ref bean="rememberMeServices"/>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/login.jsp?errorId=1"/>
<property name="defaultTargetUrl" value="/"/>
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>
<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="changeThis"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/denied.jsp"/>
</bean>
</property>
</bean>
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager">
<bean class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
</property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/secure/admin/**=ROLE_ADMIN
/secure/**=IS_AUTHENTICATED_REMEMBERED
/**=IS_AUTHENTICATED_ANONYMOUSLY
</value>
</property>
</bean>
<bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="tokenValiditySeconds" value="1800"></property>
<property name="key" value="changeThis"/>
</bean>
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key" value="changeThis"/>
</bean>
<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="changeThis"/>
</bean>
</list>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="userCache">
<bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache">
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</property>
<property name="cacheName" value="userCache"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userProperties">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="/WEB-INF/users.properties"/>
</bean>
</property>
</bean>
<bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/>
</beans>WEB-INF/applicationContext.xml - FilterChainProxy: Delegates Filter requests to a list of Spring-managed beans.
- HttpSessionContextIntegrationFilter: Populates the SecurityContextHolder with information obtained from the HttpSession.
- LogoutFilter: Logs a principal out.
- AuthenticationProcessingFilter: Processes an authentication form.
- SecurityContextHolderAwareRequestFilter: A Filter which populates the ServletRequest with a new request wrapper.
- RememberMeProcessingFilter: Detects if there is no Authentication object in the SecurityContext, and populates it with a remember-me authentication token if a RememberMeServices implementation so requests.
- AnonymousProcessingFilter: Detects if there is no Authentication object in the SecurityContextHolder, and populates it with one if needed.
- AnonymousProcessingFilter: Detects if there is no Authentication object in the SecurityContextHolder, and populates it with one if needed.
- ExceptionTranslationFilter: Handles any AccessDeniedException and AuthenticationException thrown within the filter chain.
- AuthenticationProcessingFilterEntryPoint: Used by the SecurityEnforcementFilter to commence authentication via the AuthenticationProcessingFilter. This object holds the location of the login form, relative to the web app context path, and is used to commence a redirect to that form.
- AccessDeniedHandlerImpl: Used by ExceptionTranslationFilter to handle an AccessDeniedException.
- FilterSecurityInterceptor: Performs security handling of HTTP resources via a filter implementation.
- TokenBasedRememberMeServices: Identifies previously remembered users by a Base-64 encoded cookie.
- DaoAuthenticationProvider: An AuthenticationProvider implementation that retrieves user details from an UserDetailsService.
- InMemoryDaoImpl: Retrieves user details from an in-memory list created by the bean context.