Integrating Spring Security 3.1 and JSF 2.0

Today in this tutorial we will integrate our JSF 2.0 application with Spring Security 3.1.

We will be using the basic security feature of Spring Security and later on we will expand on this.

What we need

  1. JDK
  2. Eclipse
  3. Spring Core
  4. Spring Security
  5. Some additional jars

Project Creation

  1. Create a new “Dynamic Web Project” In Eclipse.
  2. Add all the required libraries as shown below.
  3. Required Libraries

  4. Under “WebContent” create a folder called as “pages
  5. Create four folders under pages namely home, index, secure, unsecure
  6. Folder Structure For Pages

First under home create a new xhtml file named as home.xhtml. It is a simple page with one text field and one command button for navigating.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
</h:head>
<h:body>
	<h:form id="homePageFormId">
		<h:outputLabel
			value="Select your resource, depending on selection you may be asked to login"></h:outputLabel>
		<br></br>
		<h:inputText name="option" value="#{navigator.pageToNavigate}" id="optionId" required="True" requiredMessage="Please Enter page to navigate, valid values are ToSecure and ToUnSecure"></h:inputText>
		<h:commandButton value="Navigate" action="#{navigator.navigateTo}">
		</h:commandButton>
	</h:form>
</h:body>
</html>
Second under secure folder create a new xhtml file named as secured.xhtml. We will secure this page using spring security.
secured.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
</h:head>
<h:body>
	<h:outputLabel value="I am secured, if you are seeing me it means you have logged in correctly, great!"></h:outputLabel>  
</h:body>
</html>
Third under unsecure folder create a new xhtml file named as unsecured.xhtml. This page will be public and doesn’t need any security.
unsecured.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
</h:head>
<h:body>
	<h:outputLabel value="I have no problem with anyone, I am publicly visible!"></h:outputLabel>  
</h:body>
</html>
Fourth under index folder create a jsp file named as index.jsp. This file is the default welcome-file, it will redirect it to home.jsf.
index.jsp

<% response.sendRedirect("pages/home/home.jsf"); %>
Ok, so now we are done with all the different pages that our project needs.
Now is the time to write a managed bean which will help us in navigating to secured and unsecured pages.
Create a new class named as Navigator.java as shown below:

Navigator.java

package com.mumz.jsfspringsec.beans;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name="navigator")
@SessionScoped
public class Navigator {
	private String pageToNavigate = "";
	
	public String navigateTo(){
		if("ToSecure".equalsIgnoreCase(pageToNavigate)){
			return "Secured";
		} else if("ToUnSecure".equalsIgnoreCase(pageToNavigate)){
			return "UnSecured";
		} else {
			//This will never happen but we will use this to extend this application
			return "none";
		}
	}
	
	public String getPageToNavigate() {
		return pageToNavigate;
	}
	public void setPageToNavigate(String option) {
		this.pageToNavigate = option;
	}
}
Now, is the time to write our various configuration files.


First we will write our web.xml

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	xsi:schemaLocation=
		"http://java.sun.com/xml/ns/javaee 
     	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<!-- CONFIGURATION FILES both Bean definition and security -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
				/WEB-INF/classes/CONFIGURATION/SPRING/BEANDEFINITION/jsfspring-sec-bean-config.xml
				/WEB-INF/classes/CONFIGURATION/SPRING/SECURITY/jsfspring-sec-security-config.xml
		</param-value>
	</context-param>
	<!-- CONFIGURATION FILES END HERE -->
	<!-- PROJECT STAGE START FOR DEVELOPEMENT MARK IT AS DEVELOPMENT, FOR TESTING, UAT, PRODUCTION REMOVE THIS -->
	<context-param>
		<param-name>javax.faces.PROJECT_STAGE</param-name>
		<param-value>Development</param-value>
	</context-param>
	<!-- PROJECT STAGE END -->
	<!-- Enable JSF Servlet -->
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>
  	<!-- Enable JSF Server End-->
  	<!-- Integrate JSF and Spring -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- Integrate JSF and Spring End-->
	<!-- Enable Spring Filter, Spring Security works on the concept of Filters -->
	<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>
	</filter-mapping>
	<!-- Enable Spring Filter End -->
	<!-- Welcome File  -->
	<welcome-file-list>
		<welcome-file>/pages/index/index.jsp</welcome-file>
	</welcome-file-list>
	<!-- Welcome File End-->
</web-app>
Second lets write our navigation mechanism, remember JSF needs a valid faces-config.xml.


faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    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_2_0.xsd"
    version="2.0">
    <!-- Enable Spring -->
    <application>
    	<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
    <!-- Simple Navigation Rule -->
    <!-- If  user keys in ToSecure, move to /pages/secure/secured.xhtml-->
    <!-- Else If user keys in ToUnSecure, move to /pages/unsecure/unsecured.xhtml-->
    <navigation-rule>
    	<display-name>pages/home/home.xhtml</display-name>
    	<from-view-id>/pages/home/home.xhtml</from-view-id>
    	<navigation-case>
    		<from-action>#{navigator.navigateTo}</from-action>
    		<from-outcome>Secured</from-outcome>
    		<to-view-id>/pages/secure/secured.xhtml</to-view-id>
    		<redirect></redirect>
    	</navigation-case>
    </navigation-rule>
    <navigation-rule>
    	<display-name>pages/home/home.xhtml</display-name>
    	<from-view-id>/pages/home/home.xhtml</from-view-id>
    	<navigation-case>
    		<from-action>#{navigator.navigateTo}</from-action>
    		<from-outcome>UnSecured</from-outcome>
    		<to-view-id>/pages/unsecure/unsecured.xhtml</to-view-id>
    		<redirect></redirect>
    	</navigation-case>
    </navigation-rule>
</faces-config>
Third now we start with Spring and Spring Security.

Create a folder called as Resource under your project and few other child folder as shown in the below image. This way we can breakup our configuration files across multiple directories for better maintainability.

Resources Folder Structure

With our configuration folder structure in place, lets write our Spring related configuration files.


First we will write our bean configuration file. Create a new xml file under BEANDEFINITION folder and name it jsfspring-sec-bean-config.xml. This contains only one bean definition which is our Navigator bean.

jsfspring-sec-bean-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:sec="http://www.springframework.org/schema/security"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
       http://www.springframework.org/schema/security
	   http://www.springframework.org/schema/security/spring-security-3.1.xsd">
       
	<beans:bean id="navigator" name="navigator" class="com.mumz.jsfspringsec.beans.Navigator" scope="session">
	</beans:bean>
</beans:beans>
Second lets write our main Spring Security configuration. Create a new xml file under SECURITY folder and name it as jsfspring-sec-security-config.xml

jsfspring-sec-security-config.xml

 <?xml version="1.0" encoding="UTF-8"?>
<beans:beans 
	xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:beans="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	   http://www.springframework.org/schema/security
	   http://www.springframework.org/schema/security/spring-security-3.1.xsd">
	   
	 <sec:http auto-config="true" use-expressions="true">
		<sec:intercept-url pattern="/pages/secure/**" access="hasRole('ROLE_USER')" />
		<sec:intercept-url pattern="/pages/unsecure/**" access="permitAll"/>
		<sec:intercept-url pattern="/**" access="permitAll"/>
	</sec:http>
	
	<sec:authentication-manager alias="authenticationManager">
		<sec:authentication-provider>
			<sec:user-service>
				<sec:user authorities="ROLE_USER" name="admin" password="admin" />
			</sec:user-service>
		</sec:authentication-provider>
	</sec:authentication-manager>	
</beans:beans>
In the above configuration we have restricted user for /pages/secure/**, which means to access these resources you have to login with username = admin and password = admin, for non-secure location /pages/unsecure/** there is no need for logging into the system.

If you followed all the steps your final project structure should resemble like:
That’s it, we are now ready to see Spring Security 3.1 and JSF 2.0 in action.

In the next post we will see how can we move away from hardcoded userid and password to a database based authentication.

There are 12 comments

  1. AFerns

    Hi, I got the jars and all the source files in place in the eclipse project. However deployment to tomcat 7.0.35 gives a SEVER: Error listenerStart
    SEVERE: Context [/dynamicspringsec] startup failed due to previous errors.

  2. Karan

    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/jsfspring-sec-security-config.xml]; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.getLocalName(Lorg/w3c/dom/Node;)Ljava/lang/String;
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:420)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:92)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:123)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:422)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:352)
    at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4791)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    Caused by: java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.getLocalName(Lorg/w3c/dom/Node;)Ljava/lang/String;
    at org.springframework.security.config.SecurityNamespaceHandler.parse(SecurityNamespaceHandler.java:71)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1297)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1287)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:135)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:92)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:507)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:398)
    … 23 more

    Please Help me rectifying the issues…

  3. Luna

    I think you might have gotten your files mixed up. In your
    web.xml, you defined the context parameter: contextConfigLocation
    /WEB-INF/classes/CONFIGURATION/SPRING/BEANDEFINITION/jsfspring-sec-bean-config.xml
    /WEB-INF/classes/CONFIGURATION/SPRING/SECURITY/jsfspring-sec-security-config.xml
    However, your CONFIGURATION file is located in your resources
    folder. Is this a problem?

  4. Luna

    I have another question. What’s the point of your
    bean-config file? I deleted it and the web application worked fine.
    Thank you very much for your website, by the way. It’s been
    incredibly helpful to me in learning spring security!

  5. chandu

    Thank you very much superb work. I need one more help from you sir please explain how to use tiles.xml in JSF ..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s