Spring Security Custom Login Form Annotation Example

In this tutorial, we will convert previous Spring Security custom login form (XML) project to a pure annotation-based project.

Technologies used :

  1. Spring 3.2.8.RELEASE
  2. Spring Security 3.2.3.RELEASE
  3. Eclipse 4.2
  4. JDK 1.6
  5. Maven 3
  6. Tomcat 7 (Servlet 3.x)
Note
In this example, last Spring Security hello world annotation example will be reused, enhance it to support a custom login form.

1. Project Demo

2. Directory Structure

Review the final directory structure of this tutorial.

spring-security-custom-login-annotation-directory

3. Spring Security Configuration

Spring Security configuration via annotations.

SecurityConfig.java

package com.mkyong.config;

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 SecurityConfig extends WebSecurityConfigurerAdapter {

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

	//.csrf() is optional, enabled by default, if using WebSecurityConfigurerAdapter constructor
	@Override
	protected void configure(HttpSecurity http) throws Exception {

	    http.authorizeRequests()
		.antMatchers("/admin/**").access("hasRole('ROLE_USER')")
		.and()
		    .formLogin().loginPage("/login").failureUrl("/login?error")
		    .usernameParameter("username").passwordParameter("password")		
		.and()
		    .logout().logoutSuccessUrl("/login?logout")
		.and()
		    .csrf(); 		
	}
}

The equivalent of the Spring Security XML file :


	<http auto-config="true">
	  <intercept-url pattern="/admin**" access="ROLE_USER" />
	  <form-login 
		login-page="/login" 
	        default-target-url="/welcome" 
		authentication-failure-url="/login?error" 
		username-parameter="username"
		password-parameter="password" />
	  <logout logout-success-url="/login?logout" />
	  <!-- enable csrf protection -->
	  <csrf/>
	</http>

	<authentication-manager>
	  <authentication-provider>
	    <user-service>
		<user name="mkyong" password="123456" authorities="ROLE_USER" />
	    </user-service>
	  </authentication-provider>
	</authentication-manager>

4. Custom Login Form

4.1 Page to display the custom login form. If CSRF protection is enabled, remember to add ${_csrf.token} in both login and logout form.

login.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Page</title>
<style>
.error {
	padding: 15px;
	margin-bottom: 20px;
	border: 1px solid transparent;
	border-radius: 4px;
	color: #a94442;
	background-color: #f2dede;
	border-color: #ebccd1;
}

.msg {
	padding: 15px;
	margin-bottom: 20px;
	border: 1px solid transparent;
	border-radius: 4px;
	color: #31708f;
	background-color: #d9edf7;
	border-color: #bce8f1;
}

#login-box {
	width: 300px;
	padding: 20px;
	margin: 100px auto;
	background: #fff;
	-webkit-border-radius: 2px;
	-moz-border-radius: 2px;
	border: 1px solid #000;
}
</style>
</head>
<body onload='document.loginForm.username.focus();'>

	<h1>Spring Security Custom Login Form (Annotation)</h1>

	<div id="login-box">

		<h2>Login with Username and Password</h2>

		<c:if test="${not empty error}">
			<div class="error">${error}</div>
		</c:if>
		<c:if test="${not empty msg}">
			<div class="msg">${msg}</div>
		</c:if>

		<form name='loginForm'
		    action="<c:url value='j_spring_security_check' />" method='POST'>

		    <table>
			<tr>
				<td>User:</td>
				<td><input type='text' name='user' value=''></td>
			</tr>
			<tr>
				<td>Password:</td>
				<td><input type='password' name='pass' /></td>
			</tr>
			<tr>
			        <td colspan='2'>
                                <input name="submit" type="submit" value="submit" />
                                </td>
			</tr>
		   </table>

		   <input type="hidden" 
                     name="${_csrf.parameterName}" value="${_csrf.token}" />
		</form>
	</div>

</body>
</html>

4.2 Page to display the welcome message, default page.

hello.jsp

<%@page session="false"%>
<html>
<body>
	<h1>Title : ${title}</h1>	
	<h1>Message : ${message}</h1>	
</body>
</html>

4.3 This page is password protected, only authenticated user is allowed to access.

admin.jsp + logout

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>
<html>
<body>
	<h1>Title : ${title}</h1>
	<h1>Message : ${message}</h1>

	<c:url value="/j_spring_security_logout" var="logoutUrl" />

		<!-- csrt support -->
	<form action="${logoutUrl}" method="post" id="logoutForm">
		<input type="hidden" 
			name="${_csrf.parameterName}"
			value="${_csrf.token}" />
	</form>
	
	<script>
		function formSubmit() {
			document.getElementById("logoutForm").submit();
		}
	</script>

	<c:if test="${pageContext.request.userPrincipal.name != null}">
		<h2>
			Welcome : ${pageContext.request.userPrincipal.name} | <a
				href="javascript:formSubmit()"> Logout</a>
		</h2>
	</c:if>

</body>
</html>

5. Spring MVC Controller

A simple controller.

HelloController.java

package com.mkyong.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

	@RequestMapping(value = { "/", "/welcome**" }, method = RequestMethod.GET)
	public ModelAndView welcomePage() {

		ModelAndView model = new ModelAndView();
		model.addObject("title", "Spring Security Custom Login Form");
		model.addObject("message", "This is welcome page!");
		model.setViewName("hello");
		return model;

	}

	@RequestMapping(value = "/admin**", method = RequestMethod.GET)
	public ModelAndView adminPage() {

		ModelAndView model = new ModelAndView();
		model.addObject("title", "Spring Security Custom Login Form");
		model.addObject("message", "This is protected page!");
		model.setViewName("admin");

		return model;

	}

	//Spring Security see this :
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public ModelAndView login(
		@RequestParam(value = "error", required = false) String error,
		@RequestParam(value = "logout", required = false) String logout) {

		ModelAndView model = new ModelAndView();
		if (error != null) {
			model.addObject("error", "Invalid username and password!");
		}

		if (logout != null) {
			model.addObject("msg", "You've been logged out successfully.");
		}
		model.setViewName("login");

		return model;

	}

}

6. Initializer Classes

Here are the Initializer classes to make this a pure annotation-based project.

6.1 Initializer class to enable the Spring Security configuration.

SpringSecurityInitializer.java

package com.mkyong.config.core;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

6.2 Initializer class to enable the Spring MVC.

SpringMvcInitializer.java

package com.mkyong.config.core;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.mkyong.config.AppConfig;

public class SpringMvcInitializer 
	extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] { AppConfig.class };
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return null;
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}
	
}
AppConfig.java

package com.mkyong.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
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.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@Configuration
@ComponentScan({ "com.mkyong.web.*" })
@Import({ SecurityConfig.class })
public class AppConfig {

	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver
                           = new InternalResourceViewResolver();
		viewResolver.setViewClass(JstlView.class);
		viewResolver.setPrefix("/WEB-INF/pages/");
		viewResolver.setSuffix(".jsp");
		return viewResolver;
	}
	
}

7. Demo

7.1. Welcome Page – http://localhost:8080/spring-security-loginform-annotation/

spring-security-custom-login-annotation-welcome

7.2 Try to access /admin page, your custom login form is displayed.

spring-security-custom-login-annotation-login

7.3. If username and password is incorrect, display /login?error.

spring-security-custom-login-annotation-error

7.4. If username and password are correct, Spring will redirect the request to the original requested URL and display the page.

spring-security-custom-login-annotation-admin

7.5. Try to log out, it will redirect to /login?logout page.

spring-security-custom-login-annotation-logout

Download Source Code

References

  1. Spring Security Hello World Annotation Example
  2. Creating a Custom Login Form
  3. Spring Security 3.2.0.RC1 Highlights: CSRF Protection
  4. Wikipedia : Cross-site request forgery

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter, or befriend him on Facebook or Google Plus. If you like my tutorials, consider make a donation to these charities.

Comments

avatar
29 Comment threads
10 Thread replies
2 Followers
 
Most reacted comment
Hottest comment thread
37 Comment authors
RamAman jajodiavinayakGirishshweta chugh Recent comment authors
newest oldest most voted
Premnath
Guest
Premnath

When I execute as it is, after entering username and password I am getting below error

HTTP ERROR 403

Problem accessing /login. Reason:

Expected CSRF token not found. Has your session expired?

FaisalHyder
Guest
FaisalHyder

sorry to say but this is an easy way out… u should have put css and js folders separately inside src/main/resources folder and then have shown how to use them inside our Spring Security application with @EnableWebMVC.

Ram
Guest
Ram

Hi

Thanks for the tutorial it was very helpful. But when I use the same configuration in production where I deploy the application in example.com. The redirection when I hit example.com/admin without logging in goes to localhost:8080/login. I understand why this is happening since Spring is running under localhost in the server but is there a way to fix this to redirect to example.com/login instead?

Please note I tried adding baseUrl to loginPage like loginPage(baseUrl + ‘/login’) but that results in infinite loops.

Thanks

vinayak
Guest
vinayak

Hello, How can i integrate spring security in Spring boot rest service and Java Swing(accessing API’s using Apache HttpClient)

shweta chugh
Guest
shweta chugh

Hi,
I am running two applications UI and UIManager. Both the applications are running at different port. Spring Security is implemented at UIManager application but login page is set in UI application. Spring security works fine in Chrome, but in Mozilla firefox, spring security is not working, Firefox is unable to send a request from UI app to UIManager app. And in IE browser, windows security manager pop up comes after I enter credentials in login page. Please give me the solution to this.

carlos
Guest
carlos

This solution is BAD, how to use ApplicationEvent with spring security

crewdoo
Guest
crewdoo

Hi. How can I get access to _csrf.token if i using freemarker templating engine?

Guna
Guest
Guna

You have only given the entire program, please also explain as to why we have to use these functions and what does it do..
merely just copying and pasting ur code and running the program is not helping us

Satish Pakalapati
Guest
Satish Pakalapati

HI Mkyong,

can you please explain below question :

how to prevent multiple users login with same username and password in spring?

Satish Pakalapati
Guest
Satish Pakalapati

Hi,
can you plz explain below question :

how to prevent multiple users login with same username and password in spring security.

Willians Martins
Guest
Willians Martins

Hello mkyong, I’m loving your posts, I copy this code tutorial and is running beautiful, but now, I want to use AuthenticationManagerBuilder getting the value of database, I get this code from another your tutorial, When the password is fixed(encrypt) on the database it is ok, but when I try to save in database one another user the password is write without encrypt(plaintext), can you understand me and help me?

yas
Guest
yas

This is for those of you who try to learn and make the best of it. If you are using latest spring security which is “4.0.2.RELEASE” and if you get blocked by http:xxxx/server_name/context_path/j_spring_security_check add this to http in SecurityConfig “loginProcessingUrl(“/j_spring_security_check”)” thanks to Denis Ageev and if you get blocked by http:xxxx/server_name/context_path/j_spring_security_logout There are two ways possible: 1) .logoutRequestMatcher(new AntPathRequestMatcher(“/j_spring_security_logout”)) in SecurityConfig as the same way you did /j_spring_security_check 2) you put this insetad of in admin.jsp this is because of this reason http://docs.spring.io/spring-security/site/docs/4.0.2.RELEASE/reference/html/jc.html#jc-logout it says as follow The URL that triggers log out to occur (default is /logout). If CSRF… Read more »

johaness vix
Guest
johaness vix

this is not very flexible if you need for example angular js for doing auth besides those html ugly spring tags.

Aspen Sugan
Guest
Aspen Sugan

Can anyone please reply me asap.

Jeff
Guest
Jeff

Hello Mr Mkyong

When I run the app on sever (Tomcat), in the browser I get this message “Ressource not available” , and the url is “http://localhost:8002/spring-security-loginform-annotation/”

What’s the problem ?
Please help me. Thank you

PJ
Guest
PJ

Check your main console (Tomcat) and check status of your application.

Aman jajodia
Guest
Aman jajodia

I am also facing the same probelm and its just showing servlet startup at the end

akshobhya kallapur
Guest
akshobhya kallapur

Hi , thanks for this example .. but when I give correct username and password it is going to index page, May I know y?

Guest
Guest
Guest

Thank you, good tutorial

Nacho
Guest
Nacho

Not working…

Abul Fayes
Guest
Abul Fayes

The username parameter is set as “username” and the password parameter is set as “password” in SecurityConfig.java but in the login.jsp the input fields uses “user” and “pass” for the name attributes so it didn’t work for me without changing the input name attributes to match the parameter values. Otherwise a great tutorial thanks!

Juliuz
Guest
Juliuz

Hello, the text for bad credentials and succesful logout isent in colour. Can you help?

Pawel
Guest
Pawel

wery good! It’s really worked!

Priyanka Kulathilaka
Guest
Priyanka Kulathilaka

I got error and posted here it

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project : Compilation failure: Compilation failure:
[ERROR] ..SecurityConfig.java:[12,7] error: cannot access Filter
[ERROR] ..SpringSecurityInitializer.java:[5,7] error: cannot access ServletException
[ERROR] -> [Help 1]

http://stackoverflow.com/questions/25073074/error-cannot-access-filter

Girish
Guest
Girish

javax.servlet
javax.servlet-api
${javax.servlet.version}
provided

scope is incorrect, it was
provider

Tushar Bhatt
Guest
Tushar Bhatt

I am not able to deploy the application in Tomcat, on researching I found that there is an error in SecurityConfig.java file. Error is : ”
Could not autowire. No beans of ‘AuthenticationManagerBuilder’ type found.
“. Can somebody help me to resolve the issue.

Virmerson Santos
Guest
Virmerson Santos

To function correctly, and avoid 404 error I switched j_spring_security_logout by “/ logout” and also j_spring_security_check by “/ login” in login.jsp file and admin.jsp

Denis Ageev
Guest
Denis Ageev

add this to http in SecurityConfig “loginProcessingUrl(“/j_spring_security_check”)”

RaunakShakya
Guest
RaunakShakya

thank you. your suggestion helped me solve a problem that had been cracking my brains for the past few days.

Philip Ardley
Guest
Philip Ardley

The ” spring-security-custom-login-form-annotation.zip (19 KB)” can’t be imported to Eclipse ad run on Tomcat 7. These are the steps I took to make the imported (from file system) project work: 1) In the pom.xml change the javax.servlet.version from “3.1.0” to “3.0.1”. 2) In the pom.xml change the jdk.version from “1.6” to “1.7”. 3) Open the project properties, -> Java Build Path -> Libraries, remove all those links to the maven repository. 4) Right click your project, -> Configure -> Convert to maven. 5) Make sure your Tomcat library is in Java Build Path -> Libraries and also is the target… Read more »

Guest
Guest
Guest

The “last Spring Security hello world annotation example” can’t be imported to Eclipse ad run on Tomcat 7. These are the steps I took to make the imported (from file system) project work: 1) In the pom.xml change the javax.servlet.version from “3.1.0” to “3.0.1”. 2) In the pom.xml change the jdk.version from “1.6” to “1.7”. 3) Open the project properties, -> Java Build Path -> Libraries, remove all those links to the maven repository. 4) Right click your project, -> Configure -> Convert to maven. 5) Make sure your Tomcat library is in Java Build Path -> Libraries and also… Read more »

Lurkin
Guest
Lurkin

This tutorial is not working :(

I got red mark in eclpse on………..

1.@ComponentScan({ “com.mkyong.web.*” })

2.auth.inMemoryAuthentication().withUser(“mkyong”).password(“123456”).roles(“USER”);

3.AbstractAnnotationConfigDispatcherServletInitializer

4.AbstractSecurityWebApplicationInitializer

The jars I’m using are:

javax.servlet-api-3.0.1.jar

jstl-1.2.jar

org.springframework.context-3.0.4.RELEASE.jar

spring-beans-3.0.4.RELEASE.jar

spring-context-3.0.2.RELEASE.jar

spring-core-3.1.1.RELEASE.jar

spring-security-config-3.2.4.RELEASE.jar

spring-security-core-2.0.2.jar

spring-security-web-3.0.0.RELEASE.jar

spring-web-3.0.5.RELEASE.jar

spring-webmvc-4.0.5.RELEASE.jar

Raghavulu Bussa
Guest
Raghavulu Bussa

Hi,

I’m getting

No mapping found for HTTP request with URI [/{app-context}/j_spring_security_check] in DispatcherServlet with name ‘dispatcher’ when using the java config while it worked with xml config

Bono
Guest
Bono

I’ve got the same error. Nice example but not working properly.

spring-context, spring-webmvc: 4.0.5.RELEASE
spring-security-core, spring-security-config, spring-security-web: 3.2.4.RELEASE

Juan Manuel Flores
Guest
Juan Manuel Flores

Same error. Could you make it work?

RiccardoC
Guest
RiccardoC

as far as I know, this example won’t work with Spring Security 3.2.x; they changed some urls.

j_spring_security_check is now /login (POST only, unless you configure it differently)

you have to provide your mapping for logout, this is is accomplished by something like (after usernameParameter(“username”).passwordParameter(“password”)):

.and().logout().logoutUrl(“/logout”);

JukaBarros
Guest
JukaBarros

If you use Spring Security 3.2.x you should change “j_spring_security_check” to “login” and “j_spring_security_logout” to “logout”

And the parameter (username and password) in SecurityConfig.java must have the same name in input in login.jsp. Example:

SecurityConfig.java

usernameParameter(“name”).passwordParameter(“pass”)

login.jsp

User:

Password:

??????? ???????
Guest
??????? ???????

Hi, why you don’t use the latest spring 4.

johaness vix
Guest
johaness vix

he doesn’t know how to.