Spring Security login form integration example with Java configuration

This tutorial will show you how to integrate Spring Security into a Spring MVC application using purely Java configuration. The application won’t use a database, the user credentials are just defined as plain text in the configuration class for simplicity.

We will show you almost all of the files needed for the working application, but will only describe the parts that are strictly connected to the Spring Security integration and not just there to have a basic webapp up and running.

At the end of the tutorial you can download the whole working Maven project.

Tools used:

  • Spring Tool Suite 3.7.1.RELEASE
  • Apache Maven 3.3.9
  • Java 8
  • Tomcat 8.0.32

Looking for an XML configuration solution?

We have a similar article that uses XML configuration.

The project structure

Here is the final structure of the project:

jtuts-2016-03-01-spring-security-simple-integration-java-config-project-structure

Dependencies

Aside from Spring that we are using to build our webapp, we need two Spring Security related dependencies:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>jtuts</groupId>
    <artifactId>jtuts-spring-security-java-config</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>jtuts-spring-security-java-config</name>

    <properties>

        <!-- Dependency versions -->

        <spring.version>4.2.5.RELEASE</spring.version>
        <spring.security.version>4.0.4.RELEASE</spring.security.version>

        <servlet.api.version>3.1.0</servlet.api.version>
        <jsp.version>2.0</jsp.version>

        <!-- Plugin versions -->

        <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>

        <!-- Misc versions -->

        <java.version>1.8</java.version>

    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp.version}</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>jtuts-spring-security-java-config</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>

    </build>

</project>

The important things to note here:

  • spring-security-web: Contains filters and related web-security infrastructure code. You’ll need it if you require Spring Security web authentication services and URL-based access-control. This also pulls in the spring-security-core dependency.
  • spring-security-config: Contains the security namespace parsing code. You need it if you are using the Spring Security XML namespace for configuration.

We also needed to set the failOnMissingWebXml property to false, because by default STS would report an error that we don’t have a web.xml.

The springSecurityFilterChain filter

One of the core elements of Spring Security is a chain of filters that determine if the user has access to a given resource. If we are using Java configuration, setting this up is really easy, we just have to create a class extending the AbstractSecurityWebApplicationInitializer class.

package com.jtuts.web.configuration.security;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    
}

It will configure the filter in a way that is equivalent to setting it up in the web.xml like this:

<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>
  <dispatcher>ERROR</dispatcher>
  <dispatcher>REQUEST</dispatcher>
</filter-mapping>

Creating the configuration class for Spring Security

Most of the configuration goes into our SpringSecurityConfiguration class:

package com.jtuts.web.configuration.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("admin").password("Secret123").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        
        http.authorizeRequests()
            .antMatchers("/login").access("permitAll")
            .antMatchers("/**").access("hasRole('ROLE_USER')")
        .and().formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/home")
            .failureUrl("/login?error=true")
        .and().logout()
            .logoutSuccessUrl("/login");
    }

}

Let’s see in detail the meaning of this code:

  • The @Configuration annotation is needed so that Spring knows that this class may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
  • The @EnableWebSecurity annotation means that security configuration is configured in this class.
  • In the configureGlobal method we define a dummy user, that is only stored in memory for the sake of simplicity.
  • In the configure method we set the following:
    • The /login URL is accessible to anyone, but all other URLs are just for those having the USER role.
    • We use a form for login, it’s URL is /login, and if the login successful we redirect to /home and if it is unsuccessful, we redirect to /login?error=true.
    • When the user logs out successfully, we redirect them to /login.

This configuration is imported in the main application configuration:

package com.jtuts.web.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.jtuts.web.configuration.security.SpringSecurityConfiguration;

@EnableWebMvc
@Configuration
@Import(SpringSecurityConfiguration.class)
public class ApplicationConfiguration extends WebMvcConfigurerAdapter  {

}

The login form

The view file for the login form is login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JTuts Spring Security integration tutorial with XML configuration</title>
</head>
<body>
    <h1>Login</h1>

    <form action="${pageContext.request.contextPath}/login" method="POST">
        <table>
            <tr>
                <td><label for="username">Username:</label></td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td><label for="password">Password:</label></td>
                <td><input type="password" name="password" /></td>
            </tr>
            <tr>
                <td><input type="submit" value="Login" /></td>
            </tr>
        </table>
        <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> 
    </form>
</body>
</html>

The important things to note here are the following:

  • The URL where we post the form (/login) is the default URL in Spring Security 4.
  • The names of the fields (username, password) are also the default names expected by Spring Security.
  • The hidden field is there because CSRF protection is enabled by default and we need a token to be able to submit the form.

Warning: In earlier versions of Spring Security the defaults mentioned could be different, so please pay attention to that if you are on an older version.

Other files

We don’t have a web XML so we use a WebApplicationInitializer to configure the application:

package com.jtuts.web.configuration;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class CustomWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {

        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(ApplicationConfiguration.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(DispatcherServletConfiguration.class);

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));

        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

We also have a configuration file for the dispatcher servlet, that just sets up a view resolver and the packages to scan for components:

package com.jtuts.web.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@ComponentScan(basePackages = "com.jtuts.web")
public class DispatcherServletConfiguration {

    @Bean
    ViewResolver setupViewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");

        return resolver;
    }
    
}

Other then the code I showed you, we have a very simple view file for the home page. And also two basic ontrollers for the home and login page.

Result

When you deploy the project to a Tomcat 8, you get a simple login form and when you log in with the right credentials it redirects you the the home page.

login-form

Download

You can download the working project from here.

Spring Security login form integration example with XML configuration

This tutorial will show you how to integrate Spring Security into a Spring MVC application. The application won’t use a database, the user credentials are just defined as plain text in the configuration file for simplicity.

We will show you almost all of the files needed for the working application, but will only describe the parts that are strictly connected to the Spring Security integration and not just there to have a basic webapp up and running.

At the end of the tutorial you can download the whole working Maven project.

Tools used:

  • Spring Tool Suite 3.7.1.RELEASE
  • Apache Maven 3.3.9
  • Java 8
  • Tomcat 8.0.32

The project structure

Here you can see the structure of the finished project.

project-structure

Dependencies

Aside from Spring that we are using to build our webapp, we need two Spring Security related dependencies.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>jtuts</groupId>
    <artifactId>spring-security-simple-integration-xml</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>jtuts-spring-security-xml</name>

    <properties>

        <!-- Dependency versions -->

        <spring.version>4.2.5.RELEASE</spring.version>
        <spring.security.version>4.0.4.RELEASE</spring.security.version>

        <servlet.api.version>3.1.0</servlet.api.version>
        <jsp.version>2.0</jsp.version>

        <!-- Plugin versions -->

        <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>

        <!-- Misc versions -->

        <java.version>1.8</java.version>

    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp.version}</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>jtuts-spring-security-xml</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

The important things to note here:

  • spring-security-web: Contains filters and related web-security infrastructure code. You’ll need it if you require Spring Security web authentication services and URL-based access-control. This also pulls in the spring-security-core dependency.
  • spring-security-config: Contains the security namespace parsing code. You need it if you are using the Spring Security XML namespace for configuration.

The springSecurityFilterChain filter

One of the core elements of Spring Security is a chain of filters that determine if the user has access to a given resource. We define this filter in our web xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">

    <display-name>JTuts Spring Security integration tutorial with XML configuration</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/application-context.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Spring Security -->

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

</web-app>

Configuring Spring Security via XML

We put the XML configuration for Spring Security in spring-security.xml:

<?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-4.2.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd">

    <http use-expressions="true">
        <intercept-url pattern="/login" access="permitAll" />
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />

        <form-login default-target-url="/home" login-page="/login" authentication-failure-url="/login?error=true" />

        <logout logout-success-url="/login" />
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="Secret123" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>

Here a little more explanation is needed.

  • In the http element, we define the rules and settings for our security purposes:
    • The use-expressions="true" attribute means that we can provide the access rules as expressions (like permitAll), instead of the traditional set of attributes.
    • Each intercept-url element describes one rule for our security. The first rule means that we should allow everyone to access the login page, the second rule means that all other pages are protected and need the ROLE_USER role to have access.
    • In the form-login element we specify some URLs to use for the login form.
      • default-target-url: After successful login, the user will be redirected here.
      • login-page: The URL of the login page.
      • authentication-failure-url: The URL to redirect to when authentication fails.
    • In the logout element we specify the URL to redirect to when the logout is successful.
  • In the authentication-manager element in this simple example we define the valid user credentials as plain text. There are alternative configurations available for cases when for example you would like to get user details from a database.

The XML is imported in the main application context (application-context.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 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-4.2.xsd 
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd 
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
 
 	<import resource="classpath:spring/spring-security.xml"/>
 
</beans>

The login form

The view file for the login form is login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JTuts Spring Security integration tutorial with XML configuration</title>
</head>
<body>
    <h1>Login</h1>

    <form action="${pageContext.request.contextPath}/login" method="POST">
        <table>
            <tr>
                <td><label for="username">Username:</label></td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td><label for="password">Password:</label></td>
                <td><input type="password" name="password" /></td>
            </tr>
            <tr>
                <td><input type="submit" value="Login" /></td>
            </tr>
        </table>
        <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> 
    </form>
</body>
</html>

The important things to note here are the following:

  • The URL where we post the form (/login) is the default URL in Spring Security 4.
  • The names of the fields (username, password) are also the default names expected by Spring Security.
  • The hidden field is there because CSRF protection is enabled by default and we need a token to be able to submit the form.

Warning: In earlier versions of Spring Security the defaults mentioned could be different, so please pay attention to that if you are on an older version.

Other files

Other then the code I showed you, we have a very simple view file for the home page. And also two very simple controllers for the home and login. Also we have a dispatcher-servlet.xml where we configure the view resolver and the some other things.

Result

When you run the project, you get a simple login form and when you log in with the right credentials it redirects you the the home page.

login-form

Download

You can download the working project from here.

How to get new HttpSession after invalidating the current one in Spring controller?

When you invalidate your current session you can no longer perform actions on it, like adding attributes. What if you would like to add attributes to your session after you invalidated it? Of course in this case you will be adding the attributes to a new session, because the invalidated one is not longer accessible.

Because of this you could get an exception like this:

java.lang.IllegalStateException: setAttribute: Session [24420949034523442D2704B180E4342] 
has already been invalidated

To solve this, just call the request.getSession() method on your HttpServletRequest. This method will return the current session if there is one, otherwise it will create a new one, and returns that.

Here is a short example:

@RequestMapping(value = { "/doSomething"}, method = RequestMethod.POST)
public String doSomething(final HttpServletRequest request) {

    // ... some more code 

    HttpSession oldSession = request.getSession();
    oldSession.invalidate(); 

    // The session has been invalidated in the previous call so we need to get the new one.
    HttpSession newSession = request.getSession();
    newSession.setAttribute("attribute-name", "attribute-value");

    // ... some more code 
}

You can check the documentation for: getSession().

Why setting lazy bean loading in Spring does not improve a web application’s startup speed?

Lazy initialization

By default, when a Spring web application starts up, all of the defined beans are instantiated automatically. If you have a huge application with thousands of beans, this could take a considerable amount of time.

One idea to speed up the application startup could be to set the loading of beans to lazy. That means, that most beans won’t be created at the application startup. They will be created when they are required for the first time by some other code. If you are using XML configuration you can tell Spring to initialize all beans lazily by default using the default-lazy-init attribute:

<beans xmlns="..." xmlns:xsi="..." xsi:schemaLocation="..." default-lazy-init="true">
    ...
</beans>

Why it may not work?

In theory, this could work, and save us some time during the startup process. In reality however, it is possible that despite our efforts to use lazy loading, almost all beans are created at the time of startup. Let’s see why.

I mentioned earlier that lazy loading means that beans are only created when they are first required. But what if almost all beans are required at the time of application startup?

In a Spring web application controllers are responsibe for serving incoming requests.  The controllers are also Spring beans, and their methods are annotated with the routes they are mapped to. So in order to know which Controller and which method to call when a request comes in, all controllers must be instantiated during application startup.

These are just the controllers yet, but they start a “chain reaction”. Controllers require services to do their job, so the services will also get instantiated and injected into the controllers. These services probably require some kind of repositories to get data from a database. So when a service is instantiated, the referenced repositories are also created. And due to this chain reaction, we will quite surely reach the majority of the beans present in a web application. At the end of the process we have an instance of most of the beans as if we didn’t use lazy initialization.

When could it work?

If you have beans in your application that are not required directly or indirectly by beans that are created on startup, then you could benefit from lazy initialization.

This can happen for example when you are running a test case without your controllers. In this situation it is possible that almost no beans are created when the application starts up. Other possibility is if you have some kind of factories in your code that create beans based on a condition. If the condition is not met for some of your beans, they may not get created until later time.

Sources

How to create a custom Spring form errors tag

Spring has a tag library that helps you in building forms. It also has a tag called errors that lets you output validation errors in a formatted way.  However, this tag has some limitations. When multiple errors are displayed at the same time, you can only control the styling of the outside container and not the styling of each of the errors. So for example it is not possible to display the error messages as an unordered list.

This tutorial describes how to create a custom errors tag that has some extra customization attibutes compared to the basic Spring form errors tag.

The steps we will take include the following:

  • Creating a Tag Library with our custom tag definition in it.
  • Creating a custom class that specifies how our new tag will work.
  • Include the tag library in a sample JSP and use it to display errors.

Creating the Tag Library

A tag library definition is contained in a .tld file and also each tag has a class which contains the logic that results in some kind of output when we use the tag. We are using the basic Spring error tag’s definition as a starting point, but add two extra attributes to it. The added attributes are highlighted in the code below.

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
    <tlib-version>1.0</tlib-version>
    <short-name>Jtuts common tags</short-name>
    <uri>http://jtuts.com/taglib/tags</uri>
    <tag>
          <description>Renders field errors wrapped in a specified tag.</description>
          <name>errors</name>
          <tag-class>jtuts.tag.CustomSpringFormErrorsTag</tag-class>
          <body-content>JSP</body-content>
          <variable>
               <name-given>messages</name-given>
               <variable-class>java.util.List</variable-class>
          </variable>
          <attribute>
               <description>Path to errors object for data binding</description>
               <name>path</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>id</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Enable/disable HTML escaping of rendered values.</description>
               <name>htmlEscape</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Specifies the HTML element that is used to render the individual errors.</description>
               <name>innerElement</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
               <attribute>
               <description>Specifies the CSS class that the element has that is used to render the individual errors.</description>
               <name>innerElementCssClass</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Delimiter for displaying multiple error messages. Defaults to the br tag.</description>
               <name>delimiter</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Equivalent to "class" - HTML Optional Attribute</description>
               <name>cssClass</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Equivalent to "style" - HTML Optional Attribute</description>
               <name>cssStyle</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>lang</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>title</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>dir</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>tabindex</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onclick</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>ondblclick</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmousedown</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmouseup</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmouseover</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmousemove</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmouseout</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onkeypress</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onkeyup</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onkeydown</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Specifies the HTML element that is used to render the enclosing errors.</description>
               <name>element</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <dynamic-attributes>true</dynamic-attributes>
     </tag>
</taglib>

One of the new attributes is innerElement. It can be used to specify an HTML element that will be used to wrap every single error message individually. The other new attribute is the innerElementCssClass. This specifies the CSS class or classes that will be applied to each of the inner wrapping elements.

The other attributes are just copied from the original Spring form errors tag.

Creating the tag class

The tag class specifies what kind of output will the tag produce based on it’s attribute values. Here is the full class for the new tag.

package jtuts.tag;

import javax.servlet.jsp.JspException;

import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.tags.form.ErrorsTag;
import org.springframework.web.servlet.tags.form.TagWriter;


/**
 * The Spring form:errors tag extended with attributes which allow to use an inner element with a
 * css class which wraps every individual error item.
 */
public class CustomSpringFormErrorsTag extends ErrorsTag {

    private static final long serialVersionUID = 24723583545L;

    private String innerElement;
    private String innerElementCssClass;

    @Override
    protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
        tagWriter.startTag(getElement());
        writeDefaultAttributes(tagWriter);
        String delimiter = ObjectUtils.getDisplayString(evaluate("delimiter", getDelimiter()));
        String[] errorMessages = getBindStatus().getErrorMessages();

        for (int i = 0; i < errorMessages.length; i++) {
            String errorMessage = errorMessages[i];
            if (i > 0) {
                tagWriter.appendValue(delimiter);
            }

            if (innerElement != null)
                tagWriter.appendValue(startTagForInnerElement());

            tagWriter.appendValue(getDisplayString(errorMessage));

            if (innerElement != null)
                tagWriter.appendValue(endTagForInnerElement());
        }
        tagWriter.endTag();
    }

    private String startTagForInnerElement() {
        StringBuilder stringBuilder = new StringBuilder("<" + innerElement);

        if (innerElementCssClass != null)
            stringBuilder.append(" class=\"" + innerElementCssClass + "\"");

        stringBuilder.append(">");

        return stringBuilder.toString();
    }

    private String endTagForInnerElement() {
        return "</" + innerElement + ">";
    }

    public String getInnerElement() {
        return innerElement;
    }

    public void setInnerElement(String innerElement) {
        this.innerElement = innerElement;
    }

    public String getInnerElementCssClass() {
        return innerElementCssClass;
    }

    public void setInnerElementCssClass(String innerElementCssClass) {
        this.innerElementCssClass = innerElementCssClass;
    }

}

You can see that it extends the basic error tag (org.springframework.web.servlet.tags.form.ErrorsTag).  We override the renderDefaultContent method that creates the output of the tag. Anything that you pass to this method will be appended to the output produced when someone uses this tag.

As you can see the extra logic that we have added is pretty simple. We just print an opening and closing tag around each error if the user provided the innerElement attribute and we also add the CSS classes to the opening tag if the innerElementCssClass attribute is specified.

We have also set up some extra methods, but they are just there to simplify the renderDefaultContent method.

The outer element that encloses all the errors is just displayed the same way as it was in the super implementation (startTag, endTag, writeDefaultAttributes calls).

Using the tag

For demonstration purposes I have created a very simple registration form that uses the newly created tag.

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="jtuts" uri="/WEB-INF/tags/jtuts.tld" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Registration form with a custom errors tag.</title>
    <style>
        .error-message { color: #CC3344; }
    </style>
</head>
<body>
    <h1>Register</h1>
    <form:form commandName="userForm" action="${pageContext.request.contextPath}/register" method="post">
        <jtuts:errors path="*" element="ul" innerElement="li" delimiter="" cssClass="error-message" />
        <table>
            <tr>
                <td>First Name:</td>
                <td><form:input path="firstName" /></td>
            </tr>
            <tr>
                <td>Last Name:</td>
                <td><form:input path="lastName" /></td>
            </tr>
            <tr>
                <td>User Name:</td>
                <td><form:input path="userName" /></td>
            </tr>
            <tr>
                <td>Email Address:</td>
                <td><form:input path="emailAddress" /></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="Save User" />
                </td>
            </tr>
        </table>
    </form:form>
</body>
</html>

Notice the taglib definition with the taglib directive and the usage of our new jtuts:errors tag.

The POM, the Controller, the form class and validation

For the sake of completeness I also show you the remaining parts of the sample application.

The pom.xml looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>how-to-create-a-custom-spring-form-errors-tag-20150126</groupId>
    <artifactId>how-to-create-a-custom-spring-form-errors-tag-20150126</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>how-to-create-a-custom-spring-form-errors-tag-20150126 Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <spring.version>4.1.4.RELEASE</spring.version>
        <javax.validation.validation-api.version>1.1.0.Final</javax.validation.validation-api.version>
        <hibernate-validator.version>5.1.3.Final</hibernate-validator.version>
        <servlet-api.version>2.5</servlet-api.version>
        <jsp-api.version>2.2</jsp-api.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>${javax.validation.validation-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>${hibernate-validator.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>${servlet-api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp-api.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/jtuts</path>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

I just included the minimum dependencies required for this project. I also used the Tomcat Maven plugin to be able to deploy the app easily.

The Controller class:

package jtuts.controller;

import javax.validation.Valid;

import jtuts.form.UserForm;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;


@Controller
public class RegisterController {
 
    @RequestMapping(value="register", method = RequestMethod.GET) 
    public ModelAndView registerGet() {
        ModelAndView model = new ModelAndView();
        
        model.setViewName("register");
        model.addObject("userForm", new UserForm());
        
        return model;
    }

    @RequestMapping(value="register", method = RequestMethod.POST) 
    public ModelAndView registerPost(@Valid @ModelAttribute("userForm") UserForm userForm, BindingResult result) {
        ModelAndView model = new ModelAndView();
        
        model.setViewName("register");
        
        return model;
    }
}

In the controller we are just running the form through some basic validation. The validation rules are set up in the form class which looks like this:

package jtuts.form;

import org.hibernate.validator.constraints.NotEmpty;

public class UserForm {

    @NotEmpty(message="The first name may not be empty.")
    private String firstName;
    
    @NotEmpty(message="The last name may not be empty.")
    private String lastName;
    
    @NotEmpty(message="The user name may not be empty.")
    private String userName;
    
    @NotEmpty(message="The email address may not be empty.")
    private String emailAddress;

    
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
}

The Spring configuration for the validator to work:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    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-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
         
    <context:component-scan base-package="jtuts" />
    <mvc:annotation-driven validator="validator" />
  
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
  
</beans>

The working application

This is what the working application looks like when we try to submit an empty form. Notice that we are displaying the error messages as an unordered list which was not possible with the original version of the errors tag.

how-to-create-a-custom-spring-form-errors-tag-20150126-registration-form

Download

You can download the whole working source code from here.