Simple web application with Spring Security: Part 1

The purpose of this series is to examine spring security. To do this we will iteratively and incrementally develop a simple web application that uses spring security to satisfy the functional/security requires for the application.

Tools and Technologies

Servlets 2.5, JSP 2.1, HTML, CSS, Javascript
Ant 1.7.1 http://ant.apache.org/
Java 1.5 http://java.sun.com/javase/downloads/index.jsp
Spring 2.5.6 http://springframework.org/download
Spring Security 2.0.4 http://springframework.org/download
Tomcat 6.0.18 http://tomcat.apache.org/
Eclipse http://www.eclipse.org/downloads/
JUnit 4.5 http://www.junit.org/
JMock 2.5.1 http://www.jmock.org/
WebDriver http://code.google.com/p/webdriver/

The big picture

User Story 1: A user that is not logged in should be forced to authenticate themselves through a login form when they visit a secure page.

We want to be able to add the behavior described above but first we need to create the infrastructure that will support development of the web application.

Create Spring Project On Eclipse

Task 1: Setup project structure on Eclipse to develop web application

To keep things simple we will just set up one project that will contain all the code for the application. Create a new spring project called SpringSecurityWebApplicationWAR.

In package explorer, right-click, New->Other->Spring->Spring Project

Create project structure as displayed below:

springsecuritywebapplicationwarstructure

  1. Set the target-eclipse project as the output folder for the project on eclipse.
    1. Right-click on project and Select Build Path -> Configure Build Path..
    2. Select the source tab and choose target-eclipse folder as output folder at bottom

Create automated build

Task 2: Add support for building and deploying a the application to tomcat using ANT

Add dependencies to lib folder

  • Get the catalina-ant.jar from TOMCAT_HOME\lib directory and put a copy of it in the SpringSecurityWebApplicationWAR\lib\compile directory
  • Get the junit-4.5.jar file and put it into SpringSecurityWebApplicationWAR\lib\test\junit
  • Get the jmock-2.5.1 jars and put into SpringSecurityWebApplicationWAR\lib\test\jmock-2.5.1

Configure Tomcat

Ensure tomcat is configured with a user with a manager role in the tomcat-users.xml

example tomcat-users.xml might look like this

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<!-- Tomcat Roles -->
<role rolename="tomcat"/>
<role rolename="manager"/>

<!-- Tomcat Users -->
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="admin" password="admin" roles="manager"/>
</tomcat-users>

Add tomcat.properties file to SpringSecurityWebApplicationWAR

# Properties for Tomcat Server
tomcat.server=localhost
tomcat.manager.url=http://${tomcat.server}:8080/manager

# Make sure the $CATALINA_HOME/conf/tomcat-users.xml file has an
# entry with username/password admin/admin and that they have the manager role.
tomcat.username=admin
tomcat.password=admin

# example tomcat-users.xml might look like this
# <?xml version='1.0' encoding='utf-8'?>
# <tomcat-users>
#   <!-- Tomcat Roles -->
#   <role rolename="tomcat"/>

#   <role rolename="manager"/>
#
#   <!-- Tomcat Users -->
#   <user username="tomcat" password="tomcat" roles="tomcat"/>
#   <user username="admin" password="admin" roles="manager"/>
# </tomcat-users>

Create tomcatTasks.properties file

# idea taken from http://raibledesigns.com/wiki/Wiki.jsp?page=TomcatAntTasks
deploy=org.apache.catalina.ant.DeployTask
install=org.apache.catalina.ant.InstallTask
list=org.apache.catalina.ant.ListTask
reload=org.apache.catalina.ant.ReloadTask
remove=org.apache.catalina.ant.RemoveTask
resources=org.apache.catalina.ant.ResourcesTask
roles=org.apache.catalina.ant.RolesTask
start=org.apache.catalina.ant.StartTask
stop=org.apache.catalina.ant.StopTask
undeploy=org.apache.catalina.ant.UndeployTask

Create tomcat-macros.xml file

create macros around the useful ant targets supplied in the cataline-ant.jar

<project name="tomcat-macros">

<property file="tomcat.properties" />

<taskdef file="tomcatTasks.properties">
<classpath>
<pathelement
path="lib/compile/catalina-ant.jar" />
</classpath>
</taskdef>

<macrodef name="deploy-to-tomcat">
<attribute name="managerurl" default="${tomcat.manager.url}" />
<attribute name="username" default="${tomcat.username}" />
<attribute name="password" default="${tomcat.password}" />
<attribute name="appname" />
<attribute name="warfile" />
<sequential>
<deploy url="@{managerurl}" username="@{username}" password="@{password}"
path="/@{appname}" war="@{warfile}" />
</sequential>
</macrodef>

<macrodef name="undeploy-from-tomcat">
<attribute name="managerurl" default="${tomcat.manager.url}" />
<attribute name="username" default="${tomcat.username}" />
<attribute name="password" default="${tomcat.password}" />
<attribute name="appname" />
<sequential>
<undeploy url="@{managerurl}" username="@{username}"
password="@{password}" path="/@{appname}" />
</sequential>
</macrodef>

<macrodef name="reload-context-on-tomcat">
<attribute name="managerurl" default="${tomcat.manager.url}" />
<attribute name="username" default="${tomcat.username}" />
<attribute name="password" default="${tomcat.password}" />
<attribute name="appname" />
<sequential>
<reload url="@{managerurl}" username="@{username}" password="@{password}"
path="/@{appname}" />
</sequential>
</macrodef>

<macrodef name="start-on-tomcat">
<attribute name="managerurl" default="${tomcat.manager.url}" />
<attribute name="username" default="${tomcat.username}" />
<attribute name="password" default="${tomcat.password}" />
<attribute name="appname" />
<sequential>
<start url="@{managerurl}" username="@{username}" password="@{password}"
path="/@{appname}" />
</sequential>
</macrodef>

<macrodef name="stop-on-tomcat">
<attribute name="managerurl" default="${tomcat.manager.url}" />
<attribute name="username" default="${tomcat.username}" />
<attribute name="password" default="${tomcat.password}" />
<attribute name="appname" />
<sequential>
<stop url="@{managerurl}" username="@{username}" password="@{password}"
path="/@{appname}" />
</sequential>
</macrodef>

<macrodef name="list-apps-on-tomcat">
<attribute name="managerurl" default="${tomcat.manager.url}" />
<attribute name="username" default="${tomcat.username}" />
<attribute name="password" default="${tomcat.password}" />
<sequential>
<list url="@{managerurl}" username="@{username}" password="@{password}" />
</sequential>
</macrodef>
</project>

Create ANT build.xml file for project

This will be responsible for clean, compiling, testing, building the war and deploying the war to tomcat (using macros we defined).

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="springsecuritywebapp">
<import file="tomcat-macros.xml" />

<!-- properties for building application -->
<property name="app.name" value="springsecuritywebapp" />
<property name="warfile.name" value="${app.name}.war" />

<property name="lib.dir" value="${basedir}/lib" />
<property name="lib.test.dir" value="${lib.dir}/test" />
<property name="web.dir" value="${basedir}/web" />
<property name="source.dir" value="src/main/java" />
<property name="test.source.dir" value="src/test/java" />
<property name="webxml.file" value="${web.dir}/WEB-INF/web.xml" />
<property name="test.pattern" value="**/**Test.java" />

<!-- properties for generated artifacts -->
<property name="target.dir" value="target" />
<property name="classes.dir" value="${target.dir}/classes" />
<property name="classes.test.dir" value="${target.dir}/test-classes" />
<property name="report.dir" value="reports" />
<property name="test.report.dir" value="${report.dir}/tests" />

<!-- compilation settings -->
<property name="compile.debug" value="true" />
<property name="compile.deprecation" value="true" />
<property name="comiple.optimize" value="true" />

<!-- paths for dependencies used -->
<path id="build.classpath">
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</path>

<path id="junit.classpath">
<fileset dir="${lib.test.dir}">
<include name="lib/test/junit/junit-4.5.jar" />
</fileset>
</path>

<path id="jmock.classpath">
<fileset dir="${lib.test.dir}">
<include name="lib/test/jmock-2.5.1/*.jar" />
</fileset>
</path>

<path id="devenv.test.classpath">
<path refid="junit.classpath" />
<path refid="jmock.classpath" />
</path>

<target name="clean" description="cleans up ant generated classes">
<echo message="${ant.project.name} - clean" />
<delete dir="${target.dir}" failonerror="true" />
<delete dir="${classes.dir}" failonerror="true" />
<delete dir="${classes.test.dir}" failonerror="true" />
<delete dir="${report.dir}" failonerror="true" />
<delete dir="${test.report.dir}" failonerror="true" />
</target>

<target name="compile">
<echo>compiling ${ant.project.name}</echo>
<javac debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${comiple.optimize}" srcdir="${source.dir}" destdir="${classes.dir}">
<classpath>
<path refid="build.classpath" />
</classpath>
</javac>
<javac debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${comiple.optimize}" srcdir="${test.source.dir}" destdir="${classes.test.dir}">
<classpath>
<path refid="devenv.test.classpath" />
<path refid="build.classpath" />
<pathelement path="${classes.dir}" />
</classpath>
</javac>
</target>

<target name="test" depends="compile" description="run all the tests (unit)">
<junit dir="${basedir}" printsummary="false" fork="true" haltonfailure="false" errorProperty="test.failed" failureProperty="test.failed">
<sysproperty key="basedir" value="${basedir}" />
<formatter type="xml" />
<classpath>
<path refid="devenv.test.classpath" />
<path refid="build.classpath" />
<pathelement path="${classes.dir}" />
<pathelement path="${classes.test.dir}" />
</classpath>
<batchtest todir="${test.report.dir}">
<fileset dir="${test.source.dir}">
<include name="${test.pattern}" />
</fileset>
</batchtest>
</junit>
</target>

<target name="war" depends="-init, compile">
<delete file="${target.dir}/warfile.name" />
<war destfile="${target.dir}/${warfile.name}" webxml="${webxml.file}">
<fileset dir="${web.dir}">
<include name="**/*.xml" />
<include name="**/*.htm" />
<include name="**/*.html" />
<include name="**/*.jsp" />
</fileset>
<lib dir="lib">
<exclude name="compile/*.jar" />
<exclude name="source/*.jar" />
<exclude name="test/*.jar" />
<include name="**/*.jar" />
</lib>
<classes dir="${classes.dir}" />
<zipfileset dir="resources/images" prefix="images" />
<zipfileset dir="resources/css" prefix="css" />
<zipfileset dir="${web.dir}/WEB-INF" prefix="WEB-INF/classes">
<include name="*.properties" />
</zipfileset>
</war>
</target>

<target name="deploy" depends="war" description="deploy war to tomcat">
<!--
         NOTE: some oddity when using ${tomcat.username} and ${tomcat.password}
         Its like their values are not being picked up correctly and authentication fails and
         a 401 server error is returned for the deployment
       -->
<deploy-to-tomcat username="admin" password="admin" appname="${app.name}" warfile="target\${warfile.name}" />
</target>

<target name="undeploy" description="undeploy war from tomcat">
<undeploy-from-tomcat username="${tomcat.username}" password="${tomcat.username}" appname="${app.name}" />
</target>

<target name="stop" description="stop war application on tomcat">
<stop-on-tomcat username="${tomcat.username}" password="${tomcat.username}" appname="${app.name}" />
</target>

<target name="start" description="start war application on tomcat">
<start-on-tomcat username="${tomcat.username}" password="${tomcat.username}" appname="${app.name}" />
</target>

<target name="reload" description="reload app on tomcat">
<reload-context-on-tomcat username="${tomcat.username}" password="${tomcat.username}" appname="${app.name}" />
</target>

<target name="list" description="list all applications on tomcat">
<list-apps-on-tomcat username="${tomcat.username}" password="${tomcat.username}" />
</target>

<!-- private ant targets -->
<target name="-init">
<echo>creating project structure and classpaths for
       ${ant.project.name}</echo>
<mkdir dir="${target.dir}" />
<mkdir dir="${classes.dir}" />
<mkdir dir="${classes.test.dir}" />
<mkdir dir="${report.dir}" />
<mkdir dir="${test.report.dir}" />
</target>
</project>

Add a mimimal web.xml file

Add a mimimal web.xml file to web\WEB-INF folder

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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-app_2_5.xsd"
version="2.5">

<display-name>Spring security web application (series)</display-name>

<!--
to specifically stop trouble with multiple apps on tomcat
-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>springsecuritywebapp_root</param-value>
</context-param>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

Create basic index.jsp file

Create the index.jsp file under the web folder (not web/WEB-INF/jsp)

<%@ page session="true"%>
<html>

<head>
<title>Index: Simple Web Application (with Spring Security)</title>
</head>

<body>

Yes the simple web application is running correctly.

</body>

</html>

Invoke ant deploy target

Invoke the ant deploy target (from eclipse or command line) to generate the .war file and deploy it to your local tomcat. If everything has worked correctly you should be able to see the contents of index.jsp at http://localhost:8080/springsecuritywebapp/

After running the ant war or deploy target, the project file structure should look like the following:
springsecuritywebapplicationafterwartarget

Summary

In summary, we have created the infrastructure needed to aid development of the simple web application.

  • project structure on eclipse
  • build automation through ant
  • ability to deploy to a web container so we can run/demo and test the application

In the next part we will describe how we add an automated acceptance/functional test. We will configure the application to use spring MVC and spring security

The code for this part is tagged and available for viewing online at: http://code.google.com/p/spring-security-series/source/browse/#svn/tags/SpringSecuritySeriesWAR-Part1

SVN Url: https://spring-security-series.googlecode.com/svn/tags/SpringSecuritySeriesWAR-Part1

Advertisements

17 Responses to “Simple web application with Spring Security: Part 1”

  1. Mau Nguyen Says:

    I use svn to co the source but not success. Would you please give me the instruction to get the source or get the zip file of all the source because I would like to learn and to trial.
    Best regards,
    Mau Phung Nguyen

  2. Me, myself and I Says:

    In build.xml, in target deploy, you use

    username=”admin” password=”admin”

    It should be:

    username=”${tomcat.username}” password=”${tomcat.password}

    Also, for some reason the undeploy target doesn’t seem to work – which is reasonable to expect, since the undeploy target (and many others) contains:

    password=”${tomcat.username}”

  3. Me, myself and I Says:

    Update: undeploy task doesn’t work anyway. ALthough generated URL is correct – if I paste it into a virgin browser window, I’m prompted for a user name and a password and the undeployment proceeds after I enter the exactly same ones I wrote into tomcat.properties.

  4. Nitin Singhal Says:

    In build.xml : project name=”springsecuritywebapp”
    should be modified as project name=”springsecuritywebapp” default=”war” to create the successfull build.

  5. Tung Lam Says:

    Can you help me about this, when i try to access the SVN, it ask me the username and password, please send me username and pass, thanks in advacne

  6. Rendy Says:

    Hi,
    Great tutorial.
    May I asking for username and password for download through svn since it block anonymous user.

    Warmy regards,
    Rendi

  7. Mike Bergin Says:

    Great tutorial so far. One problem is there is a backslash used in the deploy target which bombs on Linux.

    Using a / in the value for warfile fixes it: warfile=”target/${warfile.name}”.

  8. Arrchana Says:

    Awesome tutorial!
    Has helped me get a very clear picture of Spring 🙂

  9. Greg Dicovitsky Says:

    The same / trick as above is needed for Mac OS/X (no surprise.)

  10. Trigger Says:

    I can’ t able to open checkout code ..(read only)..please Upload surce code again

  11. lkafle Says:

    wonderful

  12. Spring Security – Parte I « pajarokillo Says:

    […] https://heraclitusonsoftware.wordpress.com/software-development/spring/simple-web-application-with-sp… […]

  13. Learner Says:

    Hey it seems very good tutorial to begin with. Can you let me know that what perspective should we choose to develop this project in eclipse. I mean should we use Java perspective or JavaEE perspective ?

  14. peliz Says:

    Hi
    To run through the ant build, you should start tomcat server first. Otherwise, the connection failure occurs

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s


%d bloggers like this: