Simple web application with Spring Security: Part 3

In this part, we are going to continue adding functionality/behavior to the simple web application in a test-driven manner. The next story we need to tackle is user story 2:

User Story 2: With a valid username/password a user should be able to log in and view the application home page.

Remember from part 2, we have already set up two users with appropriate roles through the spring security configuration file. We should be able to successfully login with one of the users username and password details.

Add acceptance test for user story 2 behaviour

We create an acceptance test using junit and WebDriver like we did in part 2:

package com.heraclitus.springsecuritywebapp;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.After;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

/**
 * I contain end-to-end acceptance/functional tests for the simple web
 * application with spring security.
 *
 * I specifically contain tests that verify the behaviour of UserStory 2.
 */
public class UserStory2AcceptanceTest {

    private final WebDriver driver = new FirefoxDriver();

    @After
    public void aTearDownMethodForEachTest() {
        // close firefox browser that was launched after each test
        driver.close();
    }

    @Test
    public void shouldBeAbleToViewHomePageWhenSuccessfullyAuthenticated() {

        // try to get to home.htm
        driver.get("http://localhost:8080/springsecuritywebapp/home.htm");

        // check we are on springs login page
        assertThat(driver.getTitle(), is("Login Page"));

        final WebElement usernameField = driver.findElement(By
                .name("j_username"));
        usernameField.sendKeys("username");

        final WebElement passwordField = driver.findElement(By
                .name("j_password"));
        passwordField.sendKeys("password");

        passwordField.submit();

        // state verification
        assertThat(driver.getTitle(),
                is("Home: Spring Security Web Application"));
    }
}

This test simply enters the username and password details of the user username that we setup in our applicationContext-security.xml file, into the proper form fields and submits it for authentication. If authentication is successful we should reach the home page which we verify at the end.

Running the test from within eclipse, we see that the test passes successfully.

More User Stories to complete…

Our over night build passed and automatically deployed the application to a ‘staging’ server where the client/customer can view our progress so far. The customer saw that we had passing ‘Acceptance Tests’ for User Story 1 and 2. The customer added the following stories to our product backlog.

User Story 3: An application specific login page should be used to enable authentication.

User Story 4: Users that visit the root URL should be directed straight to the application home page

User Story 3: Custom login page for form Authentication

We need to use a application specific login page/form instead of the one provided by spring security.

Our first step is to create our failing acceptance test:

package com.heraclitus.springsecuritywebapp;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.After;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

/**
 * I contain end-to-end acceptance/functional tests for the simple web
 * application with spring security.
 *
 * I specifically contain tests that verify the behaviour of UserStory 3.
 */
public class UserStory3AcceptanceTest {

    private final WebDriver driver = new FirefoxDriver();

    @After
    public void aTearDownMethodForEachTest() {
        // close firefox browser that was launched after each test
        driver.close();
    }

    @Test
    public void shouldUseApplicationLoginPageWhenAuthenticationIsRequired() {

        // try to get to home.htm
        driver.get("http://localhost:8080/springsecuritywebapp/home.htm");

        // check we are on springs login page
        assertThat(driver.getTitle(),
                is("Login: Spring Security Web Application"));
    }
}

this fail as it is currently seeing Spring Securities login page and not our application specific page that we are about to create.

The next step is to create the login.jsp page itself and place it under web/WEB-INF folder.

<%@ page session="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>

<head>
<title>Login: Spring Security Web Application</title>
</head>

<body onload='document.loginForm.j_username.focus();'>

<form id="loginForm" name="loginForm" action="j_spring_security_check" method="post">
        <table>
          <tr><td>Username</td><td><input id="usernameField" type="text" name="j_username" /></td></tr>
          <tr><td>Password</td><td><input id="passwordField" type="password" name="j_password" /></td></tr>

          <tr><td colspan="2" align="right"><input type="submit" value="Login" /></td></tr>
        </table>
</form>

</body>

</html>

Things to note here are:

  1. the action is <b>j_spring_security_check</b>
  2. the username field is explicitly named <b>j_username</b>
  3. the password field is explicitly named <b>j_password</b>

If the form isn’t setup like so, spring security will not be used for authentication.

After creating the login.jsp page, we not have to update our security configuration in applicationContext.xml.

update the http configuration to look like the following:

<http auto-config="true">
   <intercept-url pattern="/login.jsp" filters="none" />
   <intercept-url pattern="/**" access="ROLE_USER" />
   <form-login login-page="/login.jsp" default-target-url="home.htm" 
          always-use-default-target="true" />
</http>
  1. For the login.jsp page, we have disabled any filters (and thus disabled security on it)
  2. We have set the login page as login.jsp
  3. Set the default success url to be the home page
  4. Configured it so after login, the user is always taken to the home page (always-use-default-target=”true”)

Build the war and deploy and run our acceptance tests.

Our latest test passed but user story 1 and 2’s tests failed. I need to fix them up to expect the application specific login page and not the spring security login page. When I look at user story 1’s test I see that it is identical to the test i’ve written. I decide to change the specification of user story 3 to be a implementation note on the first user story. I run acceptance tests for user story 1 and 2 and they now pass.

User Story 4:

User Story 4: Users that visit the root URL should be directed straight to the application home page

Oh this is a simple one. For this we just need to put a redirect on the specified welcome file to go to the home page. That together with an acceptance test should only take about 15 minutes!

add acceptance test(s)

@Test
    public void shouldBeTakenToHomePageWhenVisitingApplicationRootURLAndAlreadyAuthenticated() {
        
        driver.get("http://localhost:8080/springsecuritywebapp/login.jsp");
        
        // login
        final WebElement usernameField = driver.findElement(By
                .name("j_username"));
        usernameField.sendKeys("username");
        
        final WebElement passwordField = driver.findElement(By
                .name("j_password"));
        passwordField.sendKeys("password");
        
        passwordField.submit();
        
        // goto application root url
        driver.get("http://localhost:8080/springsecuritywebapp/");
        
        // state verification
        assertThat(driver.getTitle(),
                is("Home: Spring Security Web Application"));
    }
    
    @Test
    public void shouldBeTakenToLoginPageWhenVisitingApplicationRootURLAndNotAuthenticated() {
        
        driver.get("http://localhost:8080/springsecuritywebapp/");
        
        // state verification
        assertThat(driver.getTitle(),
                is("Login: Spring Security Web Application"));
    }

edit index.jsp page to be:

<%@ page session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
<c:redirect url="home.htm"/>

Build and deploy and verify all acceptance tests are passing.

We will deploy the application to staging for feedback on the completed stories.

In the next part of the series we will continue to add functionality to the application. So far it has been relatively easy to add the security functionality to the application thanks to spring mvc and spring security’s configuration.

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-Part3

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

10 Responses to “Simple web application with Spring Security: Part 3”

  1. KhoaTran Says:

    I think the correct default-target-url is =”/home.htm”

    And the login.jsp is put in web folder not web/WEB-INF

  2. Siyamed Says:

    Hi,

    with the configuration

    “<form-login login-page="/login.jsp" default-target-url="home.htm"

    spring requires the default-target-url to either start with "http(s)" or "/. Therefore with the current version of spring one needs to correct the config as

    "<form-login login-page="/login.jsp" default-target-url="/home.htm"

  3. Mike Bergin Says:

    Reference to modifying http properties references incorrect filename, applicationContext.xml which seems like it should be applicationContext-security.xml.

  4. Vezir Says:

    Siyamed,

    Thanks for the correction.

  5. Deepak Says:

    After following the series for Spring security , i am always getting the below error:

    description The requested resource (/springapp/login.jsp) is not available.

    Any idea will be really helpful.

    Thanks
    Deepak

  6. Horacio Iturbe Says:

    Thanks, all this is helpful. from here I’m learning spring security

    About

    As we’re using spring-mvc the login.jsp should be /login.htm

  7. Ranjan Says:

    Hi,

    I am doing login page using spring security3. But when I am entering wrong credentials I want the username to be displayed in the login page after login failure. But I m not able to do so. So if anyone pls let me know.

  8. Terence Says:

    Thanks, helped me out heaps. Just added the default-target-url=”home.htm”
    attribute to the form-login element. Sweet.

  9. ash Says:

    Hi,

    I’m using Spring 3 and I’m facing this problem. My http configuration is as follows.

    I’m not able to login and go to the /Abc/User page. Anything I’m doing wrong here? Please help.

    Thanks,
    -ash

  10. ash Says:

    Somehow the code didn’t appear in my last post. here goes the http config..

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


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: