Simple web application with Spring Security: Part 4

In part 4, we continue to use spring security to add more behavior to our application.

In this part of the series, we look at handling authentication failure.

Specifically we will add the behavior described in user story 5 and 6 to the application.

Handling authentication failure

User Story 5: Users that fail authentication should be presented with the login page and a reason for the failure.

Note: The username used to login should remain present on the form.

The first step is to add our failing acceptance test(s) for the story:

@Test
    public void shouldRemainOnLoginPageWithInformativeMessageWhenAuthenticationFailsDueToIncorrectPassword() {

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

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

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

        passwordField.submit();

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

        final WebElement informationMessageSection = driver.findElement(By
                .id("infomessage"));
        assertThat(informationMessageSection.getText(),
                containsString("Login failed due to: Bad credentials."));
    }

    @Test
    public void shouldRemainOnLoginPageWithInformativeMessageWhenAuthenticationFailsDueToIncorrectUsername() {

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

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

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

        passwordField.submit();

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

        final WebElement informationMessageSection = driver.findElement(By
                .id("infomessage"));
        assertThat(informationMessageSection.getText(),
                containsString("Login failed due to: Bad credentials."));
    }

    @Test
    public void shouldRemainOnLoginPageWithUsernameStillPopulatedWhenAuthenticationFails() {

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

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

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

        passwordField.submit();

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

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

        assertThat(username.getValue(), is("username_does_not_exist"));
    }

The next step is to edit our applicationContext-security.xml file to configure our authentication failure page which will also be the login page.

<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" authentication-failure-url="/login.jsp?authfailed=true"/>
	</http>

after that we need to edit our login page to handle the failure and output the spring security failure reason to the user.

<%@ 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>

<style TYPE="text/css">
.errormessage {
   color:red;
}
</style>
</head>

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

<form id="loginForm" name="loginForm" action="j_spring_security_check" method="post">
<c:if test="${not empty param.authfailed}">
    <span id="infomessage" class="errormessage" >
    Login failed due to: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.
    </span>
</c:if>
        <table>
          <tr><td>Username</td><td><input id="usernameField" type="text" name="j_username" value="<c:out value="${SPRING_SECURITY_LAST_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:
  1. We check for the presence of the ‘authfailed’ parameter, if it exists we output the failure message
  2. Spring security stores the last exception and the last username in the HttpSession so they can be accessed (also make sure @page session=”true” exists on top of page)

Build and deploy the application and run the acceptance tests. They should all be passing now.

Handling login after authentication request

When a user is directed to login page, the user should be directed to that page they tried to reach previously.

This functionality comes out of the box with spring security, we just need to make sure that we configure the login-form element correctly.

First we add our failing acceptance test:

@Test
    public void shouldForwardUserToPageTheyAttemptedToReachPreviouslyAfterSuccessfulLogin() {

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

        // login on 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();

        // verify we are not at admin and not (home)
        assertThat(driver.getTitle(),
                is(not("Home: Spring Security Web Application")));
        assertThat(driver.getTitle(),
                is("Admin: Spring Security Web Application"));

    }

next we need to add another secure page to the application, we add an admin page as we know there will be also some admin features to be added. We create a basic jsp page for admin.

<%@ 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>Admin: Spring Security Web Application</title>

</head>

<body>

Admin page: only administrative users should see this page.

</body>

</html>

and lastly we update the applicationContext-security.xml file

<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="false" authentication-failure-url="/login.jsp?authfailed=true"/>
	</http>

Note: we ensure that always-use-default-target is false (the default in spring security)

Build and deploy and run all acceptance tests. We should see green bars!

Getting the code

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

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

8 Responses to “Simple web application with Spring Security: Part 4”

  1. SDB Says:

    Hello,

    I am new to spring. I am developing an application using struts 2, spring, spring security and hibernate. My requirement is , after user authentication bu sprng security, I need to store some detils regarding the logged in user in the session variable. How can I access the session variable in Java class. Also how I can display the value of the session variable in jsp.

    Please guide me,
    SDB

  2. jahid Says:

    i am geting error:

    expected is username_does_not_exist
    got ${SPRING_SECURITY_LAST_EXCEPTION.message}

    also i am not geting the informative message after authentication failed.

  3. irene Says:

    jahid, you need to add in your login.jsp this code

  4. irene Says:

    jahid, you need to add in your login.jsp this ELIgnored=’false’

  5. Mike Bergin Says:

    The section describing creating an admin page never explicity states what the page should be named although from the unit tests it must be named admin.jsp and exist in web/WEB-INF/jsp.

  6. Helen Says:

    I couldn’t get any error message when the login is incorrect, and there is no any configuration of the admin.jsp page. and now when i correctly logged in the home.jsp page is invoked

    thanks, Helen

  7. peliz Says:

    I think I am using wrong selenium version.. In my case, I had to use username.getAttribute(“value”) rather than username.getValue() method.

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: