Using Acegi/Spring security in JSF applications

In this recipe, we will use Spring security support to develop a JSF login application. The big surprise is that we will not use the classical approach, which is very complicated and problematic.

Getting ready

We have developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library. In addition, we have used Acegi/Spring libraries, which provide support for JSF 2.0. The necessary libraries are in the book code bundle, under the /JSF_libs/Acegi-Spring JSF 2.0 folder.

How to do it...

The key of this recipe consists in using an HttpRequestDispatcher to provide support for JSF and Spring Security to function properly (JSF first, Spring after it). The bean that will map login credentials and apply the HttpRequestDispatcher is listed next:

package packt.spring.login;
import java.io.IOException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("request")
public class SpringLoginBean
{
private String user;
private String password;
private boolean storeUser = false;
private boolean logIn = false;
public String getUser()
{
return this.user;
}
public void setUser(final String user)
{
this.user = user;
}
public String getPassword()
{
return this.password;
}
public void setPassword(final String password)
{
this.password = password;
}
public boolean isStoreUser()
{
return this.storeUser;
}
public void setStoreUser(final boolean storeUser)
{
this.storeUser = storeUser;
}
public boolean isLogIn()
{
return this.logIn;
}
public void setLogIn(final boolean logIn)
{
this.logIn = logIn;
}
public String loginAction() 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();
return null;
}
}

Note

If you want you can also add a method to deal with bad credentials.

Next, configure the Spring Security Filter Chain in web.xml to process Servlet FORWARD as well as REQUEST.

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

The Spring Security configuration is accomplished in the application_security-config.xml file, listed next (the login-processing-url value is /j_spring_security_check, which is the location where the HttpRequestDispatcher will make the forward):

<?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="/forbidden.jsp">
<intercept-url
pattern="/faces/secured**"
access="ROLE_ADMIN,ROLE_GUEST" />
<intercept-url
pattern="/**"
access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login
login-processing-url="/j_spring_security_check"
login-page="/faces/login.jsp"
default-target-url="/"
authentication-failure-url="/faces/login.jsp" />
<logout
logout-url="/logout*"
logout-success-url="/" />
</http>
<!--
User:admin Password:admin
User:guest Password:guest
-->
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="admin"
password="21232f297a57a5a743894a0e4a801fc3"
authorities="ROLE_ADMIN,ROLE_GUEST" />
<user name="guest"
password="084e0343a0486ff05530df6c705c8bb4"
authorities="ROLE_GUEST" />
</user-service>
</authentication-provider>
</beans:beans>

Finally, the login.jsp page is in accordance with Spring Security's parameter naming specification. The submitted info is passed to the Spring Security Filter Chain (do not modify the j_username, j_password, _spring_security_remember_me ids).

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<f:view>
<h:form id="loginForm" prependId="false">
<h:panelGrid columns="4" footerClass="subtitle"
headerClass="subtitlebig" styleClass="medium"
columnClasses="subtitle,medium">
<f:facet name="header">
<h:outputText value="Login page:"/>
</f:facet>
<label for="j_username">
<h:outputText value="User:" />
</label>
<h:inputText id="j_username" required="true" />
<label for="j_password">
<h:outputText value="Password:" />
</label>
<h:inputSecret id="j_password" required="true" />
<label for="_spring_security_remember_me">
<h:outputText value="Remember me" />
</label>
<h:selectBooleanCheckbox
id="_spring_security_remember_me" />
<h:outputText value=" " />
<h:commandButton type="submit" id="login" action="#{springLoginBean.loginAction}" value="Login" />
</h:panelGrid>
</h:form>
<h:messages />
</f:view>

The login page will look like the following screenshot (when the secured page is forbidden you will be forwarded to this page):

How to do it...

How it works...

Well, as you can see the idea is pretty simple. Instead of the hard work that is imposed by the classical approach, you can use a simple forward to a servlet. You don't even need a JSF backing bean, because the values only need to be intercepted by Spring Security on forward. This is not a problem if you still want to take advantage of JSF converters and validations.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and it is named: Acegi_Spring_security_in_JSF_applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset