Hi,
I faced some difficulties while setting up login using Spring Security 4 login without XML configuration. I thought of writing it, so others can refer and save time while developing.
Environment used
- IDE: STS
- Database: MySql
- Project Management: Maven
- Server: Jetty (Default Server looks for Web.xml)
ImportantJAR's used
- Jackson JSON Mapper JAR version 1.9.10
- Spring Security JAR version 4.0.2 (Web,Config,Data & Taglibs)
- Spring webMVC JAR
- JPA, Spring Data, HSQL, MySQL JAR's
- Other Web Dependency JAR's
STS latest release is 3.7.1 it will create web.xml file for Spring project by default. So use
File->New->Maven Project. It won't create web.xml
Add the above mentioned JAR's to POM.
I have explained Security Config and CustomAuthenticationProvider class here, get whole project from
GIT.
SecurityConfig.java
package com.springsecurityblog.config;
/**
* Author: Ramakrishna Panni
* Class: SecurityConfig
* Details: It does configure for Login page, Logout page, Failure page and CSRF token management
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
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;
import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private AuthenticationProvider customAuthenticationProvider;
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/assets/**").permitAll()
.and()
.formLogin().loginPage("/loginPage")
.defaultSuccessUrl("/homePage")
.failureUrl("/loginPage?error")
.usernameParameter("username").passwordParameter("password")
.and().csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.logout().logoutSuccessUrl("/loginPage?logout");
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
}
What does Security Config do?
It configures Authentication provider of configure method where the Role and Authentication of request is done. It does http security configuration with which URL to permit, login URL's and it also configures whether the application needs CSRF token generation. We can configure Success handlers for login and others here too. It extends
"WebSecurityConfigurerAdapter" of Spring Framework Security.
CustomAuthenticationProvider.java
package com.springsecurityblog.config;
/**
* Author: Ramakrishna Panni
* Class: CustomAuthenticationProvider
* Details: It is used to get credentials and authorize the request with roles
*/
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import com.springsecurityblog.model.Employee;
import com.springsecurityblog.model.Role;
import com.springsecurityblog.service.AccountService;
@Component("AuthenticationProvider")
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private AccountService accountService;
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
Employee member = accountService.findAccountByUsername(username);
//For username not valid
if (member == null || !member.getEmployeeUserName().equalsIgnoreCase(username)) {
throw new BadCredentialsException("Username not found.");
}
//For password not valid
if (!password.equals(member.getPassword())) {
throw new BadCredentialsException("Wrong password.");
}
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
//Employee can have many roles
for (Role employeeRole : member.getEmpRoles()) {
setAuths.add(new SimpleGrantedAuthority(employeeRole.getAuthority()));
}
return new UsernamePasswordAuthenticationToken(member, password, setAuths);
}
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
What does CustomAuthenication Provider do?
Here we will get the login credential entered, which is captured by SecurityConfig class. By using Service methods we can get to know whether the credential is correct or wrong. Roles extends Authority, there can be many roles.
Here is the table created for Employee and Role Bean.
CREATE TABLE `tblemployee` (
`ntEmpID` bigint(20) NOT NULL AUTO_INCREMENT,
`vcEmployeeUserName` varchar(30) NOT NULL,
`vcEmailIdOff` varchar(50) NOT NULL,
`vcEmailIdPer` varchar(50) NOT NULL,
`vcEmployeeFirstName` varchar(30) NOT NULL,
`vcEmployeeMiddleName` varchar(30) DEFAULT NULL,
`vcEmployeeLastName` varchar(30) NOT NULL,
`vcPresentAdd` varchar(150) NOT NULL,
`vcPermanentAdd` varchar(150) NOT NULL,
`vcHomePhoneNumber` varchar(10) DEFAULT NULL,
`vcOfficeNumber` varchar(10) NOT NULL,
`vcSkills` varchar(100) DEFAULT NULL,
`vcPassword` varchar(4000) NOT NULL,
`fsCV` tinyblob,
`fsEmployeePic` tinyblob,
PRIMARY KEY (`ntEmpID`),
UNIQUE KEY `UQ__tblEmplo__993FFD3B15DA3E5D` (`vcEmailIdPer`),
UNIQUE KEY `UQ__tblEmplo__96FFB61918B6AB08` (`vcEmailIdOff`),
UNIQUE KEY `UQ__tblEmplo__ED923BE51B9317B3` (`vcEmployeeUserName`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1
CREATE TABLE `tblemproles` (
`ntEmpRoleID` bigint(20) NOT NULL AUTO_INCREMENT,
`ntEmpID` bigint(20) NOT NULL,
`vcRole` varchar(45) NOT NULL,
PRIMARY KEY (`ntEmpRoleID`),
UNIQUE KEY `uni_empID_role` (`vcRole`,`ntEmpID`),
KEY `ntEmpID` (`ntEmpID`),
CONSTRAINT `tblemproles_ibfk_1` FOREIGN KEY (`ntEmpID`) REFERENCES `tblemployee` (`ntEmpID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1
loginPage.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.3.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>
</head>
<body ng-app='Login' onload='document.loginForm.username.focus();'>
<h3>TMS</h3>
<c:if test="${not empty error}"><div>${error}</div></c:if>
<c:if test="${not empty message}"><div>${message}</div></c:if>
<form name='login' action="<c:url value='/loginPage' />" method='POST'>
<table>
<tr>
<td>UserName:</td>
<td><input type='text' name='username' ng-model="login.username" value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' ng-model="login.password" /></td>
</tr>
<tr>
<td colspan='2'><button type="submit">Submit</button></td>
</tr>
</table>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
homePage.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- ... -->
<title>Home</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap 3.3.5 -->
<!--Favicon Image -->
</head>
<body class="hold-transition skin-blue sidebar-mini">
<sec:authorize access="hasRole('ANONYMOUS')">
<c:redirect url="/loginPage"/>
</sec:authorize>
<p>Welcome, <sec:authentication property="principal.employeeUserName"/></p><br/>
<c:url value="/logout" var="Signout" />
<form id="logout" action="${Signout}" method="post" >
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
After Login
We land in Home page. There we can get all values through
"principal"object using Security tags.
Thanks,