 | Level: Intermediate Bilal Siddiqui (xml4java@yahoo.co.uk), Freelance consultant, WaxSys
19 Feb 2008 Bilal Siddiqui continues his
series
by showing you how to use Acegi to secure Java™Server Faces (JSF)
applications. Configure JSF and Acegi to work together in a servlet container, and
explore how JSF and Acegi components cooperate with one another.
The first three parts of this
article series
introduce you to using Acegi Security System to secure Java enterprise
applications:
-
Part 1
explains how to use Acegi's built-in filters to implement a simple URL-based
security system.
-
Part 2
shows how to write an access-control policy, store it in an LDAP directory
server, and configure Acegi to interact with the LDAP server to implement the
policy.
-
Part 3
shows how to use Acegi to secure access to Java class instances in your
enterprise applications.
This fourth installment demonstrates how to use Acegi to secure JavaServer Faces
(JSF) applications that run in a servlet container. This article first explains
the features Acegi provides for this purpose and dispels some common
misconceptions about using Acegi with JSF. This article then presents a simple
web.xml file that you can use to deploy Acegi to secure a JSF application. From
there, go deep inside Acegi and JSF components to understand the sequences of
events that happen when you deploy the web.xml file and when users access the JSF
application. The article concludes by presenting a sample JSF application secured
by Acegi.
Adding security without writing Java
code
Think back to this series' first sample Acegi application (see the section "A
simple Acegi application" in
Part 1).
That application uses Acegi to provide the following security features:
- Present a login page whenever an unauthenticated user tries to access a
protected resource.
- Redirect an authorized user directly to the required protected resource.
- Present an access-denied page if the user is not authorized to access the
protected resource.
Recall that you don't need to write any Java code to get these features. You
only need to configure Acegi. You should be able to implement the same features
from Acegi in a JSF application without writing any Java code.
Clearing up misconceptions
Some other writers seem to suggest that integrating Acegi with JSF requires the
JSF application to serve the login page (see Resources).
This isn't true. It is Acegi's responsibility to serve the login page whenever
required. It is also Acegi's responsibility to make sure that the login page is
served only once during a secure session. Authenticated and authorized users can
then access a protected resource without performing the login procedure
repeatedly.
If you use JSF to serve the login page, two main problems occur:
- You don't leverage Acegi's capability to serve the login page whenever
required. You must write Java code to implement all the logic to serve the login
page.
- You must write at least some Java code to hand over user credentials
(username and password) from JSF's login page to Acegi.
Acegi is intended to be an alternative to writing Java security code. Using JSF
to serve the login page defeats this purpose and initiates a host of other
JSF-Acegi integration problems, all of which are due to the fact that Acegi is
meant to provide configurable security. If you try to use JSF to do some of
Acegi's work, you'll run into trouble.
The rest of this article explains and demonstrates that you can develop JSF
applications independently of Acegi and later configure Acegi to secure your JSF
applications — without writing any Java code. Let's start by looking at a
web.xml file that you can deploy to secure your JSF applications.
Deploying Acegi to secure a JSF
application
Listing 1 shows a web.xml file (often called a
deployment descriptor) you can use to deploy Acegi for the purpose of
securing JSF applications running inside a servlet container (such as Apache
Tomcat):
Listing 1. The web.xml file to deploy Acegi and JSF in a servlet container
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/acegi-config.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
com.sun.faces.config.ConfigureListener
</listener-class>
</listener>
<!-- Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup> 1 </load-on-startup>
</servlet>
<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<!-- Acegi filter configuration -->
<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>
<!-- Acegi Filter Mapping -->
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
|
Note that Listing 1 contains the following tags:
- Three
<context-param> tags
- Two
<listener> tags
- A
<filter> tag
- A
<servlet> tag
- A
<servlet-mapping> tag
- A
<filter-mapping> tag
Read on to learn the purpose of each of these tags in a JSF-Acegi application.
Supplying context parameters to Acegi
and JSF
Each <context-param> tag in
Listing 1 defines a parameter that either Acegi or JSF
requires during startup or execution. The first parameter —
contextConfigLocation
— defines the location of Acegi's XML configuration file.
The javax.faces.STATE_SAVING_METHOD and
javax.faces.CONFIG_FILES parameters are required by
JSF. The javax.faces.STATE_SAVING_METHOD parameter
specifies whether you prefer to store JSF page-view state on the client or the
server. The default behavior of Sun's JSF reference implementation is to store JSF
views on the server.
The javax.faces.CONFIG_FILES parameter specifies the
addresses of configuration files that JSF requires. The details of JSF
configuration files are beyond this article's scope. (See
Resources for links to material covering this topic.)
Configuring listeners for Acegi and
JSF
Now look at the two <listener> tags in
Listing 1.
<listener> tags define listener classes
that listen to and handle certain events that occur during the startup and
execution of a JSP or a servlet application. Examples include:
- The servlet container creates a new servlet context while starting a JSP or
servlet application. This event fires every time a JSP or servlet application
starts.
- The servlet container creates a new servlet request object. This event occurs
every time the container receives an HTTP request from a client.
- A new HTTP session has been established. This event occurs when a requesting
client establishes an HTTP session with the servlet container.
- A new attribute has been added to servlet context, servlet request, and HTTP
session objects.
- An existing attribute of the servlet context, servlet request, or HTTP
session objects has been modified or deleted.
The <listener> tag is like an
extensibility mechanism that allows applications running inside servlet containers
to do processing in conjunction with certain events. The servlet specification
defines several interfaces that listener classes implement in order to handle
events.
For example, the Spring Framework implements a servlet interface named
javax.servlet.ServletContextListener. The spring class
that implements this interface is named
org.springframework.web.context.ContextLoaderListener.
Note that it is the listener class in Listing 1's first
<listener> tag.
Similarly, JSF implements a class named
com.sun.faces.config.ConfigureListener, which
implements several event-listening interfaces. You can find the
ConfigureListener class in the second
<listener> tag in
Listing 1.
This article later explains the different event-listener interfaces and the
processing performed inside Acegi and JSF event-listener classes (see
"Starting up a JSF-Acegi application" and
"Processing a request for an Acegi-protected JSF page").
Configuring and mapping servlet
filters
Now look at the <filter> tag in
Listing 1. Servlet applications use filters to pre-process
incoming requests before the requested servlet can process them. Acegi uses
servlet filters to authenticate users before request processing.
Notice from the <filter> tag in
Listing 1 that its
<filter-class> child specifies a class
named org.acegisecurity.util.FilterToBeanProxy. The
FilterToBeanProxy class is part of Acegi. The class
implements an interface named javax.servlet.Filter,
which is part of the servlet specification. The
javax.servlet.Filter interface has a method named
doFilter(), which the servlet container calls upon
receipt of a request.
Also note that Listing 1's
<filter> tag has another child tag named
<init-param>. The
<init-param> tag specifies the parameters
required to instantiate the FilterToBeanProxy class.
You can see from Listing 1 that the
FilterToBeanProxy class needs just one parameter, which
is an object of a class named FilterChainProxy. The
FilterChainProxy class represents the entire chain of
Acegi filters that discusses in
Part 1
(see the "Security Filters" section). The
FilterToBeanProxy class's
doFilter() method uses the
FilterChainProxy class to execute the chain of Acegi's
security filters.
The <filter-mapping> tag in
Listing 1 specifies the request URLs that invoke Acegi's
FilterToBeanProxy. I have simply mapped all the JSF
pages to Acegi's FilterToBeanProxy. This means that the FilterChainProxy
doFilter() method automatically gets control whenever a
user tries to access a JSF page.
Configuring the JSF servlet
The <servlet> tag in a web.xml file
specifies the servlet (a JSF servlet, in this case) that you wish to invoke from a
specific URL. The <servlet-mapping> tag
defines the URL. Almost all JSP or servlet applications contain these two tags, so
there's no need to discuss them here. (See Resources for
links to material that discusses servlet programming in general.)
You've now seen all the tags the web.xml file needs in order to deploy Acegi to
secure your JSF applications. You understand how listeners, filters, and servlets
coordinate with one another. As you can guess from this discussion, if you deploy
the web.xml file in Listing 1 in a servlet container, both
Acegi and JSF try to do some processing on two occasions:
- When you start up your application
- When your application receives a request for a JSF page
The next two sections explain the sequence of events that occurs in each of
these instances.
Starting up a JSF-Acegi application
Figure 1 shows the sequence of events that happens when a
JSF-Acegi application starts up:
Figure 1. Sequence of events when
a JSF-Acegi application starts
In detail, the sequence of events shown in Figure 1 is as
follows:
- The servlet container instantiates all listeners configured in the web.xml
file.
- The servlet container registers Acegi's
ContextLoaderListener as a listener class, which
implements an interface named
javax.servlet.ServletContextListener. The
ServletContextListener interface contains two
important methods: contextInitialized() and
contextDestroyed():
- The
contextInitialized() method gains control
whenever a servlet context is initialized.
- Similarly, the
contextDestroyed() method is
called along with destruction of the servlet context when the application
exits.
- The servlet container registers JSF's
ConfigureListener as another listener. JSF's
ConfigureListener implements a number of listener
interfaces, such as ServletContextListener,
ServletContextAttributeListener,
ServletRequestListener, and
ServletRequestAttributeListener. You've already seen
the ServletContextListener interface's methods. The
remaining interfaces are:
-
ServletContextAttributeListener, which contains
three methods: attributeAdded(),
attributeRemoved(), and
attributeReplaced(). Respectively, these methods
gain control whenever an attribute is added to the servlet context, removed
from the servlet context, or replaced by a new attribute. The
attributeReplaced() method gains control in step
8 of the
processing a request for an Acegi-protected JSF page
sequence.
-
ServletRequestListener, which contains methods
that gain control whenever a new servlet request object is created or
deleted. The servlet request object represents and wraps a request from a
user.
-
ServletRequestAttributeListener, which contains
methods that gain control whenever an attribute of the request object is
added, deleted, or replaced. This article later discusses the processing
that JSF's ConfigureListener performs when a new
servlet request object is created in step 3 of the
processing a request for an Acegi-protected JSF page
sequence.
- The servlet container creates a servlet context object, which wraps
application resources (such as JSP pages, Java classes, and application
initialization parameters) and enables the entire application to access the
resources. All other components of your JSF-Acegi application (listeners,
filters, and servlets) store information about application resources in the form
of attributes in the servlet context object.
- The servlet container notifies Acegi's
ContextLoaderListener that the servlet context is
initialized by calling ContextLoaderListener's
contextInitializated() method.
- The
contextInitialized() method parses Acegi's
configuration file, creates a Web application context for the JSF-Acegi
application, and instantiates all the security filters as well as Java beans
configured in the Acegi configuration file. These filter objects are later used
in authentication and authorization when your JSF application receives a request
from a client. (See the discussion of Web application context creation that
accompanies Figure 1 in
Part 3.)
- The servlet container notifies JSF's
ConfigureListener about the initialization of the
servlet context by calling its contextInitialized()
method.
- The
contextInitialized() method checks all the JSF
managed beans configured in JSF configuration files to ensure that the Java
classes exist against each of the beans.
- The servlet container checks the web.xml file for any filters configured. For
example, the web.xml file in Listing 1 contains an Acegi
filter named
FilterToBeanProxy, which the servlet
container instantiates, initializes, and registers as a filter. Acegi is now
ready to process incoming requests for authentication and authorization.
- The servlet container instantiates the faces servlet, which starts listening
for incoming requests from users.
The next section explains the sequence of events that occurs when your JSF-Acegi
application receives a request from a user.
Processing a request for an
Acegi-protected JSF page
You've learned how to configure Acegi to secure a JSF application. You've also
seen the sequence of events that take place when you start up a JSF-Acegi
application. This section describes how JSF and Acegi components work within the
framework of a servlet container when a user sends a request for an Acegi-secured
JSF page.
Figure 2 illustrates the sequence of events that happen when a
client sends a request for a JSF page protected by Acegi:
Figure 2. JSF and Acegi working
together to serve a JSF page
In detail, the sequence of events shown in Figure 2 is as
follows:
- The servlet container creates a servlet request object representing the
user's request.
- Recall from step 3 of the
starting up a JSF-Acegi application sequence that JSF's
ConfigureListener implements the
ServletRequestListener interface. This means that
ConfigureListener listens for events related to the
creation and deletion of servlet request objects. Therefore, the servlet
container invokes the ConfigureListener class's
requestInitialized() method.
- The
requestInitialized() method prepares to
execute the JSF life cycle for the request. The preparation includes checking
whether a faces context exists for the request. The faces context wraps
information about application resources that the faces servlet will later
require to execute the JSF life cycle. The faces context is missing if this
request is the first of a new session. In this case, the
requestInitialized() method creates a new faces
context.
- The servlet container checks whether the user's request accompanies any state
information. If the servlet container finds no state information, it assumes
that the request is the first of a new session and creates an HTTP session
object for the user. If the servlet container finds that the request contains
some state information (such as a cookie or some state information in the URL),
it restores the user's previous session based on the session information it
maintains.
- The servlet container matches the request URL with the URL pattern contained
inside the
<filter-mapping> tag's
<url-pattern> child in the deployment
descriptor. If the request URL matches the URL pattern, the servlet container
invokes Acegi's FilterToBeanProxy, registered as a
servlet filter in step 9 of Figure 1.
- Acegi's
FilterToBeanProxy uses the
FilterChainProxy class to execute the entire chain of
Acegi's security filters. Acegi's filters automatically check the HTTP session
object created in step 4 to see whether the requesting client is already
authenticated. If Acegi finds that the user is not authenticated, it serves the
login page. Otherwise it goes directly through the authorization process that
described in the "Configuring the interceptor" section of
Part 2.
- Acegi updates the servlet context with session information for the
authenticated user.
- The servlet container notifies the
attributeReplaced() method of JSF's
ConfigureListener that the servlet context has been
updated. The ConfigureListener checks if any of JSF's
beans have been changed. If it finds any changes, it updates the faces context
accordingly. However, in this case, Acegi does not change any of JSF's managed
beans during authentication, so ConfigureListener
does no processing during this call.
- If the authorization process succeeds, control is transferred to the faces
servlet, which executes the JSF life cycle and serves a response back to the
user.
Now that you know how JSF and Acegi work together to serve a JSF request, it's
time to see JSF and Acegi in action.
A sample JSF-Acegi application
The download included with this article (see Download)
contains a sample JSF-Acegi application named JSFAcegiSample that demonstrates
simple integration of Acegi with JSF. The sample application uses the web.xml file
in Listing 1.
To deploy the sample application, follow the two steps in the "Deploy and run
the application" section of
Part 1.
You also need to download and unzip jsf-1_1_01.zip from Sun's JSF site (see
Resources). Copy all the files you find in jsf-1.1.X.zip
into the WEB-INF/lib folder of your JSFAcegiSample application.
You can invoke the sample application by accessing
http://localhost:8080/JSFAcegiSample from your browser. The
JSFAcegiSample application displays an index page that
contains links to protected resources and a login page. All protected pages are
developed using JSF components, whereas Acegi serves the login page and performs
authentication and authorization.
Conclusion
In this article, you've learned how to configure Acegi to secure JSF
applications. You've also learned in detail how JSF and Acegi components work
together within the framework of a servlet container. Finally, you tried running a
sample JSF-Acegi application.
There's still some more ground to cover on the subject of implementing Acegi
security for JSF applications. The next article in this series will demonstrate
how to use Acegi to secure access to JSF's managed beans.
Download | Description | Name | Size | Download method |
|---|
| Source code for this article | j-acegi4-source.zip | 15KB | HTTP |
|---|
Resources Learn
-
Securing
Java applications with
Acegi series: Get an introduction to using Acegi Security System to secure Java
enterprise applications.
-
Acegi Security System: The Acegi Web
site is your first stop for reference documentation.
-
"JSF and Acegi": This
entry on MyFaces WIKI on JSF-Acegi integration talks about using JSF beans to
secure JSF applications.
-
"Another Faces in your Face: Acegi/JSF solution addendum"
(Tony L. Kerz, Fair Trade Java Stack Blog, February 2006) and
"Integrating Acegi with JSF"
(Victor Tatai, sometimes i feel like screaming..., October 2005): Blog entries
that discuss strategy for integrating Acegi with JSF.
-
"ACEGI JSF Components hit the stores"
(Cagatay Civici's Weblog, January 2006): This blog entry discusses developing JSF
components for authentication and authorization using Acegi.
-
Apache MyFaces forum:
See the discussion thread about the use of custom JSF tags for JSF-Acegi
integration.
-
"Story of a Servlet: An Instant Tutorial"
(Mark Andrews, java.sun.com): Learn servlet programming.
-
"Getting started with JavaServer Faces 1.2"
(Richard Hightower, developerWorks, December 2007 and January 2008): Get up to
speed quickly with the latest JSF version in this two-part tutorial.
-
"Acegi Security System for Spring Framework, Part 1"
and
"Acegi
Security System for Spring Framework, Part 2":
Check out this presentation on Acegi security.
-
"Using JSF technology for XForms applications"
(Faheem Khan, developerWorks, February 2005): This tutorial explains the JSF life
cycle in detail.
- Browse the
technology bookstore
for books on these and other technical topics.
-
developerWorks Java technology zone:
Find hundreds of articles about every aspect of Java programming.
Get products and technologies
Discuss
About the author  | |  | Bilal Siddiqui is an electronics engineer, an XML consultant, and the co-founder of WaxSys, a company focused on simplifying e-business. After graduating in 1995 with a degree in electronics engineering from the University of Engineering and Technology, Lahore, he began designing software solutions for industrial control systems. Later, he turned to XML and used his experience programming in C++ to build Web- and Wap-based XML processing tools, server-side parsing solutions, and service applications. Bilal is a technology evangelist and a frequently-published technical author.
|
Rate this page
|  |