1. Overview
In this article I am going to illustrate how to implement a simple Login Page with Spring MVC for an application that’s handling the authentication with Spring Security in the backend.2. The Login Page
Let’s start by defining a very simple login page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| <html><head></head><body> <h1>Login</h1> <form name='f' action="j_spring_security_check" method='POST'> <table> <tr> <td>User:</td> <td><input type='text' name='j_username' value=''></td> </tr> <tr> <td>Password:</td> <td><input type='password' name='j_password' /></td> </tr> <tr> <td><input name="submit" type="submit" value="submit" /></td> </tr> </table> </form></body></html> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <script type="text/javascript">function validate() { if (document.f.j_username.value == "" && document.f.j_password.value == "") { alert("Username and password are required"); document.f.j_username.focus(); return false; } if (document.f.j_username.value == "") { alert("Username is required"); document.f.j_username.focus(); return false; } if (document.f.j_password.value == "") { alert("Password is required"); document.f.j_password.focus(); return false; }}</script> |
3. Message Localization
Next – let’s localize the messages we’re using on the front end. There are types of such messages, and each is localized in a different manner:- Messages generated before the form is processed by Spring’s controllers or handlers. These messages ca be referenced in the JSP pages and are localized with Jsp/Jslt localization (see Section 4.3.)
- Messages that are localized once a page has been submitted for processing by Spring (after submitting the login form); these messages are localized using Spring MVC localization (See Section 4.2.)
3.1. The message.properties files
In either case, we need to create a message.properties file for each language we want to support; the names of the files should follow this convention: messages_[localeCode].properties.For example, if we want to support English and Spanish error messages we would have the file: messages_en.properties and messages_es_ES.properties. Note that, for English – messages.properties is also valid.
We’re going to place these two files in the project’s classpath (src/main/resources). The files simply contain the error codes and messages we need to display in different languages – for example:
1
2
3
4
5
6
7
| message.username=Username requiredmessage.password=Password requiredmessage.unauth=Unauthorized access!!message.badCredentials=Invalid username or passwordmessage.sessionExpired=Session timed outmessage.logoutError=Sorry, error login outmessage.logoutSucc=You logged out successfully |
3.2. Configuring Spring MVC Localization
Spring MVC provides a LocaleResolver that works in conjunction with its LocaleChangeInterceptor API to make possible the display of messages in different languages, depending on the locale setting. To configure localization – we need to define the following beans in our MVC configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| @Overridepublic void addInterceptors(InterceptorRegistry registry) { LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); localeChangeInterceptor.setParamName("lang"); registry.addInterceptor(localeChangeInterceptor);}@Beanpublic LocaleResolver localeResolver() { CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); return cookieLocaleResolver;}@Beanpublic MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("classpath:messages"); messageSource.setUseCodeAsDefaultMessage(true); messageSource.setDefaultEncoding("UTF-8"); messageSource.setCacheSeconds(0); return messageSource;} |
By default, the locale resolver will obtain the locale code from the HTTP header. To force a default locale, we need to set it on the localeResolver():
1
2
3
4
5
6
| @Beanpublic LocaleResolver localeResolver() { CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH); return cookieLocaleResolver;} |
Alternatively there is a SessionLocaleResolver, which remembers the locale throughout the session. To use this LocaleResolver instead, we need to replace the above method with the following:
1
2
3
4
5
| @Beanpublic LocaleResolver localeResolver() { SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver(); return sessionLocaleResolver;} |
1
2
| <a href="?lang=en">English</a> |<a href="?lang=es_ES">Spanish</a> |
3.3. JSP/JSLT Localization
JSP/JSLT API will be used to display localized messages that are caught in the jsp page itself. To use the jsp localization libraries we should add the following dependencies to the pom.xml:
1
2
3
4
5
6
7
8
9
10
| <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.2-b01</version></dependency><dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version></dependency> |
4. Displaying Error Messages
4.1. Login Validation Errors
In order to use the JSP/JSTL support and display localized messages in the login.jsp lets implement the following changes in the page:1. Add the following tag lib element to the login.jsp:
1
|
1
| <fmt:setBundle basename="messages" /> |
1
2
| <fmt:message key="message.password" var="noPass" /><fmt:message key="message.username" var="noUser" /> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <script type="text/javascript">function validate() { if (document.f.j_username.value == "" && document.f.j_password.value == "") { alert("${noUser} and ${noPass}"); document.f.j_username.focus(); return false; } if (document.f.j_username.value == "") { alert("${noUser}"); document.f.j_username.focus(); return false; } if (document.f.j_password.value == "") { alert("${noPass}"); document.f.j_password.focus(); return false; }}</script> |
4.2. Pre Login Errors
Sometimes the login page will be passed an error parameter if the previous operation failed. For example, a registration form submit button will load the login page. If the registration was successful, then it would be a good idea to show a success message in the login form, and an error message if the opposite was true.In the sample login form below, we are implementing this by intercepting and regSucc and regError parameters, and displaying a localized message based on their values.
1
2
3
4
5
6
7
8
9
10
11
12
| <c:if test="${param.regSucc == true}"> <div id="status"> <spring:message code="message.regSucc"> </spring:message> </div></c:if><c:if test="${param.regError == true}"> <div id="error"> <spring:message code="message.regError"> </spring:message> </div></c:if> |
4.3. Login Security Errors
In case the login process fails for some reason, Spring Security will do a redirect to a login error URL, which we have defined to be /login.html?error=true.So – similarly to how we have shown the status of the registration in the page, we need to do the same in case of a login problem:
1
2
3
4
5
6
| <c:if test="${param.error != null}"> <div id="error"> <spring:message code="message.badCredentials"> </spring:message> </div></c:if> |
Notice that we are using a <spring:message …> element. This means that the error messages are generated during the Spring MVC processing.
The full login page – including the js validation and these additional status messages can be found in the github project.
4.4. Logout Errors
In the example that follows, the jsp code <c:if test=”${not empty SPRING_SECURITY_LAST_EXCEPTION}”> in the logout.html page will check if there was an error in the logout process.For example – if there is a persistence exception when a custom logout handler tries to store user data before redirecting to the logout page. While these errors are rare, we should handle them as neatly as possible as well.
Lets take a look at the complete logout.jsp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| <%@ taglib prefix="sec"<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}"> <div id="error"> <spring:message code="message.logoutError"> </spring:message> </div></c:if><c:if test="${param.logSucc == true}"> <div id="success"> <spring:message code="message.logoutSucc"> </spring:message> </div></c:if><html><head><title>Logged Out</title></head><body> <a href="login.html">Login</a></body></html> |
5. The Spring Security Configuration
The focus of this article is the frontend of the login process, not the backend – so we’ll look only briefly at the main points of the security configuration;
5.1. Redirecting to the Login Error URL
The following directive in the <form-login…/> element directs the flow of the application to the url where the login error will be handled:
1
| authentication-failure-url="/login.html?error=true" |
5.2. The Logout Success Redirect
1
2
| <logout invalidate-session="false" logout-success-url="/logout.html?logSucc=true" logout-url="/j_spring_security_logout" delete-cookies="JSESSIONID" /> |
6. Conclusion
In this article I have illustrated how to implement a Login page for a Spring Security backed application – handling login validation, displaying authentication errors and message localization.We’re going to be looking at a full registration implementation in the next article – with the goal of having a full implementation of the login and registration process ready for production.