"Simple Software"

Acegi/Spring Security Integration – JSF Login Page

October 9th, 2008 by Lincoln

Tutorials – What a nightmare

Everyone seems to be going through hell to get a fully functional JSF login page working with Spring Security (formerly Acegi,) and yes, I did too, but there’s an EASY way to make this happen. And get this:

  • It takes just five clear and well written lines of Java code.

First, the solution. Afterwards, the dirty details. (Spring 2.5.2 was used for this example.)
You can find a downloadable working example here. There is also a followup article on post-authentication redirecting, here.

The Solution:

public class LoginBean
{
    //managed properties for the login page, username/password/etc...
 
    // This is the action method called when the user clicks the "login" button
    public String doLogin() throws IOException, ServletException
    {
        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
 
        RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
                 .getRequestDispatcher("/j_spring_security_check");
 
        dispatcher.forward((ServletRequest) context.getRequest(),
                (ServletResponse) context.getResponse());
 
        FacesContext.getCurrentInstance().responseComplete();
        // It's OK to return null here because Faces is just going to exit.
        return null;
    }
}

—-

For anyone who was struggling because Spring Security requires you to use a Filter to intercept the login postback, thus either preventing you from being able to do JSF style validation, or visa-versa, creating a scenario where JSF can process results, but blocks Acegi from processing the request parameters.

Simply use an HttpRequestDispatcher to allow both JSf and Spring Security to function one after another. JSF goes first, then delegates work to a Spring Security  (thus preserving any request parameters that Spring Security is looking for.) After forwarding, tell JSF you have finished, and not to do any more work, immediately stop processing.

If the login credentials were bad, redirect to the Login page. If the credentials were good, redirect to the requested URL. You can even show a dynamic message for bad credentials. Add the following PhaseListener to your faces-config.xml in order to extract any login errors, and display a message to the user:

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
 
import org.springframework.security.BadCredentialsException;
import org.springframework.security.ui.AbstractProcessingFilter;
 
import uk.co.pkit.project.view.util.FacesUtils;
 
public class LoginErrorPhaseListener implements PhaseListener
{
    private static final long serialVersionUID = -1216620620302322995L;
 
    @Override
    public void beforePhase(final PhaseEvent arg0)
    {
        Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(
                AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY);
 
        if (e instanceof BadCredentialsException)
        {
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
                    AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, null);
            FacesUtils.addErrorMessage("Username or password not valid.");
        }
    }
 
    @Override
    public void afterPhase(final PhaseEvent arg0)
    {}
 
    @Override
    public PhaseId getPhaseId()
    {
        return PhaseId.RENDER_RESPONSE;
    }
 
}

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xi="http://www.w3.org/2001/XInclude"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
	<lifecycle>
		<phase-listener>login.LoginErrorPhaseListener</phase-listener>
	</lifecycle>
</faces-config>

web.xml

You must configure your Spring Security Filter Chain to process Servlet FORWARD as well as REQUESTs.

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</filter-mapping>

—-

applicationContext-security.xml

As for the Spring Security configuration, everything can be left pretty standard. The relevant parts of my configuration, for example:

<?xml version="1.0" encoding="UTF-8"?>
 
<beans:beans
	xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
                         http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                        http://www.springframework.org/schema/security
                         http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
 
	<global-method-security
		secured-annotations="enabled">
	</global-method-security>
 
	<http
		auto-config="true"
		access-denied-page="/accessDenied.jsp">
 
		<intercept-url
			pattern="/login*"
			access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<intercept-url
			pattern="/**"
			access="ROLE_USER,ROLE_ADMIN" />
 
		<form-login
			login-processing-url="/j_spring_security_check"
			login-page="/login"
			default-target-url="/"
			authentication-failure-url="/login" />
		<logout logout-url="/logout"
			logout-success-url="/" />
	</http>
 
	<authentication-provider>
                <!-- Your authentication provider here (example below)-->
	</authentication-provider>
</beans:beans>

—-

Notice here that the “login-processing-url” is set to “/j_spring_security_check”, which is the location where our HttpRequestDispatcher is going to forward to. You can call this whatever you want, but the two must match exactly.

login.xhtml / login.jspx / login.jsp

(Whatever you use as your JSF page content type, take your pick.)

So the last part of the puzzle is relatively easy. You need a JSF login page that conforms to Spring Security’s parameter naming requirements. When this page submits, its values will be forwarded to the Spring Security Filter Chain.

Notice that you don’t even need to tie the input field values to a JSF backing bean! The values only need to be intercepted by Spring Security on forward. However, if you want to do all that cool validation and stuff that JSF lets you do… go for it. I just wanted to save space in the article, and prove a point that it’s not needed.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<jsp:root
	xmlns:jsp="http://java.sun.com/JSP/Page"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	version="2.0">
 
	<f:view>
		<h:form
			id="loginForm"
			prependId="false">
			<label for="j_username"><h:outputText value="Username:" /><br />
			</label>
			<h:inputText
				id="j_username"
				required="true">
			</h:inputText>
 
			<br />
			<br />
			<label for="j_password"><h:outputText value="Password:" /><br />
			</label>
			<h:inputSecret
				id="j_password"
				required="true">
			</h:inputSecret>
 
			<br />
			<br />
			<label for="_spring_security_remember_me"> <h:outputText
				value="Remember me" /> </label>
			<h:selectBooleanCheckbox
				id="_spring_security_remember_me" />
			<br />
 
			<h:commandButton
				type="submit"
				id="login"
				action="#{loginBean.doLogin}"
				value="Login" />
 
		</h:form>
	</f:view>
</jsp:root>

Remember that Spring Security is expecting parameters to be named as they are in this file. j_username, j_password, _spring_security_remember_me. Don’t change these ids unless you change your Spring configuration.

—-

If you’re having problems

Add a LoggerListener to allow Spring Security to print messages to your logging output. This will allow you to view any error messages that may be occurring. (Note: This should be copied verbatim)

	<bean id="loggerListener"
		class="org.springframework.security.event.authentication.LoggerListener" />

—-

Finished

And that’s all it took. A simple forward to a new servlet. No JSF navigation cases, no extra configuration. Just a little J2EE, and a night of no sleep. I hope this helps a LOT of people who seem to be struggling with the task of integrating these two excellent frameworks.

Considering the forces of this problem, we really required almost no invasiveness in our normal application logic. JSF does its validation and processing without being impacted by Acegi, and Acegi can perform its magic authentication without knowing that JSF was ever the provider of its parameters.

You can see a working example of this guide here.
Enjoy.

Example Authentication Provider for Testing

	<!--
		Usernames/Passwords are rod/koala dianne/emu scott/wombat peter/opal
	-->
	<authentication-provider>
		<password-encoder
			hash="md5" />
		<user-service>
			<user
				name="rod"
				password="a564de63c2d0da68cf47586ee05984d7"
				authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
			<user
				name="dianne"
				password="65d15fe9156f9c4bbffd98085992a44e"
				authorities="ROLE_USER,ROLE_TELLER" />
			<user
				name="scott"
				password="2b58af6dddbd072ed27ffc86725d7d3a"
				authorities="ROLE_USER" />
			<user
				name="peter"
				password="22b5c9accc6e1ba628cedc63a72d57f8"
				authorities="ROLE_USER" />
		</user-service>
	</authentication-provider>

Post to Twitter Post to Delicious Post to Digg Post to StumbleUpon

Posted in JSF, Java

56 Comments

  1. john says:

    Hey guys,

    I’m trying to use spring security in my applications, but I don’t know how to add the libraries.

    I’m using NetBeans 6.5, and I have the Spring MVC 2.5 plug in. Does this plug in come with Spring Security?

    Could you please help me so that I can use Spring MVC and Security in Netbeans 6.5.
    Thank you.

  2. john says:

    I was wondering if you could tell me how to add Sp. Sec. to my NetBnzz apps. I’ve downloaded the zip, but is there a way to add it as a plug in or something. I would really appreciate it. If I can’t use it in NetBnzz what IDE should I use. Thanks

  3. Derek says:

    John we don’t used Netbeans, but I would imagine that you could check under WEB-INB/Lib(raries) and see if you see a jar called spring-security. If you do then you have it. If not go here: http://sourceforge.net/project/showfiles.php?group_id=73357&package_id=270072&release_id=630203

    The zip file is just an example Eclipse project using Acegi / Spring Security. You can copy the contents of the zip file into a sample project and try and run it.

  4. john says:

    Derek, what IDE do you use? I want to learn spring framework with the spring security. I don’t know what environment to use. Can you help me with that please?

  5. Pete says:

    Hi Lincoln,

    I have incorporated Ocpsoft’s “prettyfaces” into my application and am very enjoying “pretty” names in the address bar. However, I’m finding issues with Spring Security via LoginBean, as above. After j_spring_security_check, the forward is to the unprettyfaced resource which appears distastefully in the address bar ;-(

    Do you have any ideas how to forward to the “pretty” name?

    Also, I use a prettylink to a Spring secured resource and include a request parameter, this is not forwarded on after the j_spring_security_check.

    I do very much like prettyfaces though and love the flexibility it gives. Would be great to have a solution to fully include Spring Security.

    Cheers,
    Pete

    PS code uploaded to same url as before ;-)

  6. Lincoln says:

    Update your default-target-url, etc, in the spring config.xml file:

    default-target-url="/faces/home.jsf" authentication-failure-url="/faces/login.jsf" always-use-default-target="false" />

Leave a Comment




Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.

Search

Recent Posts

Recent Comments

OcpSoft Agile Management

Recent Tweets:

Posting tweet...

Sponsored By:

Resources

Meta