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 :
- Spring 3.2.8.RELEASE
- Spring Security 3.2.3.RELEASE
- Eclipse 4.2
- JDK 1.6
- Maven 3
- Tomcat 7 (Servlet 3.x)
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.
3. Spring Security Configuration
Spring Security configuration via annotations.
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.
<%@ 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.
<%@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.
<%@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.
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.
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.
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[] { "/" };
}
}
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/
7.2 Try to access /admin
page, your custom login form is displayed.
7.3. If username and password is incorrect, display /login?error
.
7.4. If username and password are correct, Spring will redirect the request to the original requested URL and display the page.
7.5. Try to log out, it will redirect to /login?logout
page.
Hello, How can i integrate spring security in Spring boot rest service and Java Swing(accessing API’s using Apache HttpClient)
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.
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?
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
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.
This solution is BAD, how to use ApplicationEvent with spring security
Hi. How can I get access to _csrf.token if i using freemarker templating engine?
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
HI Mkyong,
can you please explain below question :
how to prevent multiple users login with same username and password in spring?
Hi,
can you plz explain below question :
how to prevent multiple users login with same username and password in spring security.
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?
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 protection is enabled (default), then the request must also be a POST. For for information, please consult the JavaDoc.
this is not very flexible if you need for example angular js for doing auth besides those html ugly spring tags.
Can anyone please reply me asap.
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
Check your main console (Tomcat) and check status of your application.
I am also facing the same probelm and its just showing servlet startup at the end
Hi , thanks for this example .. but when I give correct username and password it is going to index page, May I know y?
Thank you, good tutorial
Not working…
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!
Hello, the text for bad credentials and succesful logout isent in colour. Can you help?
wery good! It’s really worked!
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
javax.servlet
javax.servlet-api
${javax.servlet.version}
provided
scope is incorrect, it was
provider
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.
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
add this to http in SecurityConfig “loginProcessingUrl(“/j_spring_security_check”)”
thank you. your suggestion helped me solve a problem that had been cracking my brains for the past few days.
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 runtime.
6) Run the project on the server like normal.
There are very often these types of problems with maven example projects from this website.
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 is the target runtime.
6) Run the project on the server like normal.
There are very often these types of problems with maven example projects from this website.
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
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
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:
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”);
Same error. Could you make it work?
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
Hi, why you don’t use the latest spring 4.
he doesn’t know how to.