How to get started with the Acegi framework
Posted by Okke Harsta in the early morning: March 4, 2007
How to get started with the Acegi framework and implement your own Security provider?
In the old days folks used the J2EE securing capabilities of the app server. This is of course still an option, but there are superior alternatives like the Acegi framework. Acegi is far from new and with the latest releases it has become a very stable and easy-to-use framework, especially when combined with Spring. I had to implement a custom security provider for a customer and was very surprised how easy this was accomplished. This blog describes the steps I took to get started with Acegi.
The starting point is a -very- simple web application using the Spring MVC framework. This is the web.xml:
[xml]
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd">
org.springframework.web.servlet.DispatcherServlet
[/xml]
And the blog-acegi-basic-servlet.xml configuration.
[xml]
[/xml]
The two Controllers are also very simple (for demo purposes):
[java]
public class AdminController implements Controller {
/*
* (non-Javadoc)
*
* @see org.springframework.web.servlet.mvc.Controller#handleRequest(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("admin", "info", "adminInfo");
}
}
public class PublicController implements Controller {
/*
* (non-Javadoc)
*
* @see org.springframework.web.servlet.mvc.Controller#handleRequest(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("public", "info", "publicInfo");
}
}
[/java]
As are the views admin.jsp
[xml]
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="authz" uri="http://acegisecurity.org/authz" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Hello Admin
[/xml]
and public.jsp that are placed in the folder WEB-INF/views:
[xml]
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="authz" uri="http://acegisecurity.org/authz" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Hello Public
[/xml]
For now we'll leave the acegi-security.xml empty.
[xml]
[/xml]
When you use maven2 to build your web-application you can use the jetty plugin to test it. The following pom.xml has all dependencies and the plugin configuration to use jetty with the jdk1.5:
[xml]
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
[/xml]
The transitive dependency mechanism of maven2 pulls in a lot of the Spring dependencies because of the Acegi 1.0.3 pom configuration. Because I want to use the latest release of Spring I have excluded them from Acegi. When you fire up jetty:
[code]
$ mvn jetty:start
[/code]
You can view the public and admin page with the url's http://localhost:8080/blog-acegi/site/public.html and http://localhost:8080/blog-acegi/site/admin.html. Now we will introduce Acegi into the equation. We will secure the admin page with an user/password combination, while the public page may remain unsecured. First we add an Acegi Filter to the web.xml:
[xml]
org.acegisecurity.util.FilterToBeanProxy
[/xml]
The FilterToBeanProxy is configured with a targetClass. Acegi expects an instance of this target class configured in the Spring bean context. We will code the Acegi beans in the Spring context file acegi-security.xml.
As specified in the Acegi Filter we need at least an instance of the FilterChainProxy in the acegi-secutity.xml.
[xml]
PATTERN_TYPE_APACHE_ANT
/**=exceptionTranslationFilter,filterInvocationInterceptor,authenticationProcessingFilter
]]>
PATTERN_TYPE_APACHE_ANT
/site/admin.html=ROLE_ADMIN
]]>
[/xml]
This rather large xml document configures Acegi to prevent people to see the admin part of the application. The configured beans with some details:
- The filterChainProxy defines all filters that will be called when an user requests a url that matches the Acegi Filter Chain Proxy filter mapping in the web.xml. I have configured 3 filters.
- The exceptionTranslationFilter is responsible for redirecting to the login.jsp in case of a Security Exception. Without this bean our admin page would stil be secured, but the user would get a error stacktrace when trying to access the page without being logged in.
- The authenticationProcessingFilter is responsible for handling the form post in the login.jsp. In case of failure it will redirect to the login page and in case of a successfull login the user will be redirected to the defaultTargetUrl.
- The filterInvocationInterceptor contains the actual configuration of the security restrictions. It uses two acegi decisionVoters that will check the username/password and the role of the user. In this case we limit the access to the url /site/admin.html to users with the role ROLE_ADMIN.
- The authenticationManager is the hook for all authenticationProviders we want to configure. In this example we use only one provider, but you could configure as many as you would like. I have not encountered an usecase yet where this was required.
- The authenticationProvider we use is the one which is provided by Acegi. The retrieving of the user is being delegated to an implementation of the userDetailsService
- The implementation of the userDetailsService we use is the InMemoryDaoImpl which can be configured with a list of users, password and roles. Acegi also provides a JdbcDaoImpl which actually retrieves the users and their roles from the database.
Because auto-formatting the acegi-secutiry.xml file can mess up the ANT like configuration I have coded those parts within CDATA sections. The one thing missing is the login.jsp; the login.jsp needs to be placed in the root of war file. When using maven2 for packaging the war the login.jsp must be placed in the webapp directory.
[xml]
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core_rt'%>
<%@ page import="org.acegisecurity.ui.AbstractProcessingFilter"%>
<%@ page
import="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"%>
<%@ page import="org.acegisecurity.AuthenticationException"%>
Your login attempt was not successful, try
again.
Reason: <%=((AuthenticationException) session
.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY))
.getMessage()%>
[/xml]
Again using the jetty plugin we can verify the result. When trying to access the admin page we will be prompted for an username and password and when providing the correct combination we will be forwarded to the admin page. The public page is still accessible for everyone. I have attached the source code of this example. The InMemoryDaoImpl is of course not very sophisticated. In a next blog I'll show you how to implement your own security provider.
Filed under: Security
March 15th, 2007 at 4:07 pm
hello okkke
I did not understand a thing in your example: if I directly launch the admin.jsp page how what my filter is will know that “/site/admin.html” is related to “admin.jsp” in the property objectDefinitionSource:
/site/admin.html =ROLE_ADMIN.
I will be reconnaisant if you answer me
thiks
March 15th, 2007 at 10:15 pm
Dali,
You can not access the admin.jsp page directly. You can only request it through the servlet mapping url configured in the blog-acegi-basic-servlet.xml; being /site/admin.html. The InternalResourceViewResolver links the url to a bean instance and the url is secured using the acegi interceptor.
August 1st, 2007 at 2:52 pm
Thank you for the tutorial, it helped me way.
Just a little remark for those as new to webapps as I am:
once you change the location of your context config like this:
contextConfigLocation
/WEB-INF/acegi-security.xml
your applicationContext.xml no longer gets loaded. So watch out blindly following the steps of the tutorial if you already have Spring configured.