Spring MVC Security Example

By Arvind Rai, June 25, 2019
This page will walk through Spring MVC Security example. We will create here an application with Spring Security + JPA 2 + Spring MVC + Hibernate + MySQL using annotation + XML configuration. To enable authentication using database, Spring provides UserDetailsService interface which has role to load user-specific data. We need to create a method in our DAO that will return user specific data on the basis of username entered by user on login page. We need to create a class to implement UserDetailsService and override its loadUserByUsername() method. In spring security configuration, we need to specify that class with a password encoding mechanism. In our application we will use BCryptPasswordEncoder for password encoding. We are using custom login page with custom username and password parameter with CSRF protection enabled. In JPA configuration we are not using persistence.xml file. We will see in our example how to integrate JPA in JavaConfig as well as XML configuration without persistence.xml file. We are using JPA with hibernate and hence for JpaVendorAdapter we will use its HibernateJpaVendorAdapter implementation. For transaction management using JPA, spring provides JpaTransactionManager class. In our service layer we will create a secured method. If the user is not authorized to access secured method then access denied message will be displayed to that user. For the complete demo we will create two applications, one using JavaConfig and another using XML configuration. We will have separate configuration files for spring security, JPA and spring MVC in both JavaConfig and XML configuration based application. After successful authentication, user will be displayed to user profile data. This page will be secured page by putting in WEB-INF directory. Such files can only be accessed by application controller and not by using direct URL in browser. Now let us walk through the complete example step by step.

Software Used

We are using following software in our example.
1. Java 8
2. Spring 4.3
3. Gradle 3.3
4. Maven 3.3
5. Tomcat 8.0
6. MySQL 5.5
7. Eclipse Mars

Gradle and Maven to Build the Project

Find the gradle file to build the project.
build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'war'
war.archiveName 'spring-app.war'
repositories {
    mavenCentral()
}
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web:1.5.1.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-data-jpa:1.5.1.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-security:1.5.1.RELEASE'
    compile 'mysql:mysql-connector-java:6.0.5'
    compile 'org.apache.commons:commons-dbcp2:2.1.1'
    compile 'jstl:jstl:1.2'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat:1.5.1.RELEASE'    
}  
Find the maven file to build the project.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.concretepage</groupId>
	<artifactId>spring-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>SpringSecurity</name>
	<description>Spring Demo Project</description>
	<parent>
	    <groupId>org.springframework.boot</groupId>
  	    <artifactId>spring-boot-starter-parent</artifactId>
	    <version>1.5.1.RELEASE</version>
 	    <relativePath/>
	</parent>
	<properties>
	    <java.version>1.8</java.version>
	    <context.path>spring-app</context.path>
	</properties>
	<dependencies>
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	  </dependency>
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	  </dependency>
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	  </dependency>
	  <dependency>
		 <groupId>mysql</groupId>
		 <artifactId>mysql-connector-java</artifactId>
		 <version>6.0.5</version>
	  </dependency>	  	  	  
	  <dependency>
		 <groupId>org.apache.commons</groupId>
		 <artifactId>commons-dbcp2</artifactId>
		 <version>2.1.1</version>
	  </dependency>    	  	  	  
          <dependency>
	         <groupId>jstl</groupId>
	         <artifactId>jstl</artifactId>
	         <version>1.2</version>
          </dependency>
	  <dependency>
		 <groupId>org.springframework.boot</groupId>
		 <artifactId>spring-boot-starter-tomcat</artifactId>
		 <scope>provided</scope>
	  </dependency>      
	</dependencies>
	<build>
	  <plugins>
	     <plugin>
		  <groupId>org.apache.maven.plugins</groupId>
		  <artifactId>maven-war-plugin</artifactId>
		  <version>3.0.0</version>
		  <configuration>
			 <warName>${context.path}</warName>
		  </configuration>
	     </plugin>
	  </plugins>
	</build>
</project>  

MySQL Database and JPA Entity

Find the database schema used in our example. For the demo we have inserted three users as mahesh/m123, trump/t123 with ROLE_ADMIN and one more user that is arvind/a123 with ROLE_USER in our database.
Database Schema using MySQL
-- Dumping database structure for concretepage
CREATE DATABASE IF NOT EXISTS `concretepage`;
USE `concretepage`;
-- Dumping structure for table concretepage.users
CREATE TABLE IF NOT EXISTS `users` (
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  `full_name` varchar(100) NOT NULL,
  `role` varchar(50) NOT NULL,
  `country` varchar(100) NOT NULL,
  `enabled` tinyint(1) NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- Dumping data for table concretepage.users: ~3 rows (approximately)
INSERT INTO `users` (`username`, `password`, `full_name`, `role`, `country`, `enabled`) VALUES
	('arvind', '$2a$10$Vfs8YMQx1YHI.d0x4WO8n.C.K3prfllnP2uS/j8ChpRirS17y.N42', 'Arvind Rai', 'ROLE_USER', 'India', 1),
	('mahesh', '$2a$10$N0eqNiuikWCy9ETQ1rdau.XEELcyEO7kukkfoiNISk/9F7gw6eB0W', 'Mahesh Sharma', 'ROLE_ADMIN', 'India', 1),
	('trump', '$2a$10$QifQnP.XqXDW0Lc4hSqEg.GhTqZHoN2Y52/hoWr4I5ePxK7D2Pi8q', 'Donald Trump', 'ROLE_ADMIN', 'US', 1); 
For user password encoding we are using BCrypt password encoder. It can be maximum 72 characters long, so we should keep our password column accordingly. I am using password column length as 100. If we do not set the proper length of password column then while saving BCrypt encoded password, it will be truncated and hence the user will never be authenticated because of wrong password. To create BCrypt password, we can use a simple main class.
Main.java
package com.concretepage;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class Main {
	public static void main(String[] args) {
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		System.out.println(encoder.encode("m123"));
	}
}
Now find the java entity for the users table.
UserInfo.java
package com.concretepage.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="users")
public class UserInfo implements Serializable {
	private static final long serialVersionUID = 1L;
	@Id
	@Column(name="username")
	private String userName;
	@Column(name="password")
	private String password;
	@Column(name="role")	
	private String role;
	@Column(name="full_name")	
	private String fullName;
	@Column(name="country")	
	private String country;
	@Column(name="enabled")	
	private short enabled;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	public String getFullName() {
		return fullName;
	}
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	public short getEnabled() {
		return enabled;
	}
	public void setEnabled(short enabled) {
		this.enabled = enabled;
	}
} 

Spring Security + JPA 2 + Spring MVC + Hibernate + MySQL using Annotation

In this example we will create JavaConfig files for Spring Security, JPA and Spring MVC using @Configuration annotation. We will create a service class with a secured method. We will create a DAO and there we will create a method to interact with MySQL database that will fetch user profile. In our controller class we are mapping two URL patterns, one for fetching user profile and second to display message when a user is denied access to a secured method. We are using custom login page with CSRF protection. Now find the complete details step by step.

1. Project Structure using JavaConfig in Eclipse

Find the project structure using JavaConfig in eclipse.
Spring 4 Security + JPA 2 + Spring 4 MVC + Hibernate + MySQL using Annotation + XML Example

2. JPA Configuration for Database

Find the JPA and spring integration configuration to interact with database using JavaConfig.
DBConfig.java
package com.concretepage.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration 
@EnableTransactionManagement
@PropertySource("classpath:database.properties")
public class DBConfig {
	@Autowired
        private Environment env;	
	@Bean
	public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
		LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();
		lcemfb.setJpaVendorAdapter(getJpaVendorAdapter());
		lcemfb.setDataSource(getDataSource());
		lcemfb.setPersistenceUnitName("myJpaPersistenceUnit");
		lcemfb.setPackagesToScan("com.concretepage.entity");
		lcemfb.setJpaProperties(jpaProperties());
		return lcemfb;
	}
	@Bean
	public JpaVendorAdapter getJpaVendorAdapter() {
		JpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
		return adapter;
	}
        @Bean
	public DataSource getDataSource() {
	        BasicDataSource dataSource = new BasicDataSource();
	        dataSource.setDriverClassName(env.getProperty("database.driverClassName"));
	        dataSource.setUrl(env.getProperty("database.url"));
	        dataSource.setUsername(env.getProperty("database.username"));
	        dataSource.setPassword(env.getProperty("database.password"));
	        return dataSource;
	}
	@Bean
	public PlatformTransactionManager txManager(){
		JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(
				getEntityManagerFactoryBean().getObject());
		return jpaTransactionManager;
	}
        private Properties jpaProperties() {
                Properties properties = new Properties();
                properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
                properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
                properties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
                return properties;        
        }	
} 
Let us understand what is going on in above configuration.
1. Create data source using BasicDataSource class from the commons.dbcp2 library. Using this class we configure database driver class name, URL, username and password.
2. Create entity manager factory bean using the following class.
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean 
It creates a JPA EntityManagerFactory according to the standard container bootstrap contract of JPA. In this way we get a shared EntityManagerFactory in our spring application context. To use it in DAO we just do dependency injection to get its instance. To create entity manager factory bean we specify the values to following setter methods.
setJpaVendorAdapter(): Specify an implementation of JpaVendorAdapter that will act as JPA provider. In our example we are specifying HibernateJpaVendorAdapter which is the implementation of JpaVendorAdapter that will facilitate to get hibernate EntityManager.
setDataSource(): Specify data source that will be used by JPA persistence API.
setPersistenceUnitName(): This is the default persistence unit name.
setPackagesToScan(): Specify the package name that contains JPA entities.
setJpaProperties(): Specify JPA properties that will be passed to method that creates entity manager factory as following.
Persistence.createEntityManagerFactory() 
In our example we are using hibernate persistence provider and so we are passing hibernate properties.
3. For database transaction management, spring provides JpaTransactionManager that is the implementation of PlatformTransactionManager. JpaTransactionManager is appropriate for single JPA EntityManagerFactory for transactional data access.
4. @EnableTransactionManagement enables annotation-driven transaction management capability. To handle transaction at DAO layer, we annotate DAO class or method with @Transactional annotation.

Find the database property file.
database.properties
database.driverClassName=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/concretepage
database.username=root
database.password=

hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.show_sql = true 
hibernate.format_sql = true 
Keep the database property file in the classpath. Here we have externalized database related properties and hibernate properties. This file will be read by
@PropertySource("classpath:database.properties") 
Now using spring Environment we fetch the proprieties.

3. Spring Security Configuration

Find the spring security configuration file used in our example.
SecurityConfig.java
package com.concretepage.config;  
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.concretepage.service.MyAppUserDetailsService;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private MyAppUserDetailsService myAppUserDetailsService;
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		.antMatchers("/user/**").hasAnyRole("ADMIN","USER")
		.and().formLogin()  //login configuration
                .loginPage("/customLogin.jsp")
                .loginProcessingUrl("/appLogin")
                .usernameParameter("app_username")
                .passwordParameter("app_password")
                .defaultSuccessUrl("/user/home")	
		.and().logout()    //logout configuration
		.logoutUrl("/appLogout") 
		.logoutSuccessUrl("/customLogin.jsp")
		.and().exceptionHandling() //exception handling configuration
		.accessDeniedPage("/user/error");
	} 	
        @Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(myAppUserDetailsService).passwordEncoder(passwordEncoder());
	}
        @Bean
        public PasswordEncoder passwordEncoder() {
    	        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    	        return passwordEncoder;
        }
}   
Find the uses of classes and annotations used in above spring security configuration.
1. @EnableWebSecurity annotation makes available the security configuration from WebSecurityConfigurer class to our security configuration class. If we need to override any configuration then we need to extend WebSecurityConfigurer class and override the method.
2. We have overridden configure() and configureGlobal() methods from WebSecurityConfigurer class. configure() method is used to configure HttpSecurity. Here we are configuring a custom login page, logout and access denied exception handling. By default CSRF protection is enabled.
3. configureGlobal() method will configure UserDetailsService that will authenticate user. We need to create our own class implementing UserDetailsService interface. In our example we have created MyAppUserDetailsService class for this purpose.
4. For password encoding we are using BCryptPasswordEncoder that uses BCrypt password encoding.
5. @EnableGlobalMethodSecurity annotation enables method level security. Here we have enabled @Secured annotation.

Now find the spring security initializer.
SecurityInitializer.java
package com.concretepage.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}  
It registers DelegatingFilterProxy to use the springSecurityFilterChain before any other registered Filter.

4. Spring MVC Configuration

Find the spring MVC configuration used in our example.
AppConfig.java
package com.concretepage.config;  
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration 
@ComponentScan("com.concretepage") 
@EnableWebMvc   
public class AppConfig {  
    @Bean  
    public InternalResourceViewResolver viewResolver() {  
	InternalResourceViewResolver resolver = new InternalResourceViewResolver();  
        resolver.setPrefix("/WEB-INF/secure/");  
        resolver.setSuffix(".jsp");
        return resolver;  
    }
}  
Find the configuration details.
1. InternalResourceViewResolver helps to access secure views. Secure view files are those files which reside within WEB-INF directory. Those files that are inside WEB-INF directory cannot be accessed using direct URL in browser. They can be accessed only by controllers within the application. InternalResourceViewResolver creates the URL using prefix and suffix with secure view name used by spring controller.
2. @EnableWebMvc annotation makes available spring MVC configuration from WebMvcConfigurationSupport class to our configuration class.
3. @ComponentScan annotation specifies base package name. All the classes annotated with spring stereotype such as @Service, @Controller etc will be scanned which reside within the base package and its sub packages.

Now find web application initializer class.
WebAppInitializer.java
package com.concretepage.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer  {
	@Override
	protected Class<?>[] getRootConfigClasses() {
		 return new Class[] { AppConfig.class };
	}
	@Override
        protected Class<?>[] getServletConfigClasses() {
                 return null;
        }
        @Override
        protected String[] getServletMappings() {
                 return new String[]{ "/" };
        } 
} 
The above class registers DispatcherServlet and here we need to configure our spring MVC configuration file annotated with @Configuration using getRootConfigClasses() method.

5. Authentication with UserDetailsService

To enable database level authentication in spring security, spring provides UserDetailsService interface which has the role to load user-specific data. Find the UserDetailsService implementation class.
MyAppUserDetailsService.java
package com.concretepage.service;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.concretepage.dao.UserDAO;
import com.concretepage.entity.UserInfo;
@Service
public class MyAppUserDetailsService implements UserDetailsService {
	@Autowired
	private UserDAO userDAO;
	@Override
	public UserDetails loadUserByUsername(String userName)
			throws UsernameNotFoundException {
		UserInfo activeUserInfo = userDAO.getActiveUser(userName);
		GrantedAuthority authority = new SimpleGrantedAuthority(activeUserInfo.getRole());
		UserDetails userDetails = (UserDetails)new User(activeUserInfo.getUserName(),
				activeUserInfo.getPassword(), Arrays.asList(authority));
		return userDetails;
	}
} 
On the basis of username, we fetch user profile from our DAO. Within the loadUserByUsername() method we assign username, password and roles from user profile. If password is wrong then it gives message accordingly. If username is wrong then no data is fetched from database and again it gives message accordingly.

6. Create DAO

In our DAO we have created a method that accepts username and then returns JPA entity UserInfo . Find the class.
UserDAO.java
package com.concretepage.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.concretepage.entity.UserInfo;
@Repository
@Transactional
public class UserDAO {
	@PersistenceContext	
	private EntityManager entityManager;
	public UserInfo getActiveUser(String userName) {
		UserInfo activeUserInfo = new UserInfo();
		short enabled = 1;
		List<?> list = entityManager.createQuery("SELECT u FROM UserInfo u WHERE userName=? and enabled=?")
				.setParameter(1, userName).setParameter(2, enabled).getResultList();
		if(!list.isEmpty()) {
			activeUserInfo = (UserInfo)list.get(0);
		}
		return activeUserInfo;
	}
} 
First of all we need an instance of EntityManager. Use @PersistenceContext annotation for dependency injection. EntityManager is associated with persistence context. Persistence context is the set of entity instances where their lifecycle is managed. EntityManager query the database to save entity values and fetch data to populate entity. We are calling its method createQuery() that will accept JPA query statement and return JPA Query instance.
@Transactional annotation is used to make DAO methods transactional. This annotation can be used at class level as well as method level. It has attributes to define propagation, transaction isolation level etc. We can configure these values as follows.
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation= Isolation.READ_COMMITTED)
If we do not use any attribute then their default values will be applied.

7. Create Service

Find the service class.
IUserService.java
package com.concretepage.service;
import org.springframework.security.access.annotation.Secured;
import com.concretepage.entity.UserInfo;
public interface IUserService {
	@Secured ({"ROLE_ADMIN"})
	UserInfo getDataByUserName(String userName);
} 
The method is secured and can be accessed only by ROLE_ADMIN. Now find its implementation class.
UserService.java
package com.concretepage.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.concretepage.dao.UserDAO;
import com.concretepage.entity.UserInfo;
@Service
public class UserService implements IUserService {
	@Autowired
	private UserDAO userDAO;
	public UserInfo getDataByUserName(String userName) {
		return userDAO.getActiveUser(userName);
	}
}  

8. Spring MVC Controller

Find the controller class.
UserController.java
package com.concretepage.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import com.concretepage.service.IUserService;
@Controller
@RequestMapping("/user")
public class UserController {
	@Autowired
	private  IUserService service;
	@RequestMapping(value="/home")
	public String home(ModelMap model, Authentication authentication) {
		authentication.getPrincipal();
		model.addAttribute("user", service.getDataByUserName(authentication.getName()));
 		return "user-info";
 	}
	@RequestMapping(value="/error")
	public String error() {
 		return "access-denied";
 	}
} 
Here we have two methods mapped with @RequestMapping. The method home() gives user profile and error() will be called when service layer method throws access denied exception.

9. Create Custom Login Form

Find the custom login page using CSRF protection.
customLogin.jsp
<html>
    <head>
        <title>Spring 4 Security Example</title>
    </head>
    <body>
       <h3>Spring 4 Security Example</h3>
        <font color="red">
	        ${SPRING_SECURITY_LAST_EXCEPTION.message}
        </font>
	<form action="<%=request.getContextPath()%>/appLogin" method="POST">
		Enter UserName:	<input type="text" name="app_username"/><br/><br/>
		Enter Password: <input type="password" name="app_password"/> <br/><br/>			
		<input type="submit" value="Login"/>
		<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>			
	</form>
    <body>
</html>  
The input name for username and password are custom names which we have defined in our security configuration SecurityConfig class.

10. Create Secure View Files

Find the secure views that can be accessed only by authenticated users.
WEB-INF\secure\user-info.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
    <head>
        <title>Spring 4 Security Example</title>
    </head>
    <body>
      <h3>Logged In User Detail</h3>
      <table>
        <tr><td>Name</td><td><c:out value="${user.fullName}"/></td></tr>
        <tr><td>Role</td><td><c:out value="${user.role}"/></td></tr>
        <tr><td>Country</td><td><c:out value="${user.country}"/></td></tr>
      </table>      
      <form action="<%=request.getContextPath()%>/appLogout" method="POST">
        <input type="submit" value="Logout"/>
        <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>		
      </form>      
    </body>
</html>  
The above file displays user profile.
WEB-INF\secure\access-denied.jsp
<html>
    <head>
        <title>Spring 4 Security Example</title>
    </head>
    <body>
      <h3>You are not authorized to access user profile.</h3>
      <form action="<%=request.getContextPath()%>/appLogout" method="POST">
        <input type="submit" value="Logout"/>
        <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>		
      </form> 
    </body>
</html> 
The above file shows message to an authenticated user that has been denied access to any service layer method.

Spring Security + JPA 2 + Spring MVC + Hibernate + MySQL using XML Configuration

In this example we will use XML configuration for Spring Security, JPA and Spring MVC. The DAO, service layer and controller will be same as created in the annotation based application. All our application context files will be configured in web.xml. The login page and error page will be same as created in annotation based application. Now find our XML based application step by step.

1. Project Structure using XML Configuration in Eclipse

Find the project structure using XML configuration in eclipse.
Spring 4 Security + JPA 2 + Spring 4 MVC + Hibernate + MySQL using Annotation + XML Example

2. JPA XML Configuration for Database

Find the XML namespace for JPA configuration to interact with database.
db-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

	<context:property-placeholder location="classpath:database.properties"/> 
	<bean id="basicDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${database.driverClassName}" />
		<property name="url" value="${database.url}" />
		<property name="username" value="${database.username}" />
		<property name="password" value="${database.password}" />
	</bean>
	<bean name="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        <bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
          <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
          <property name="dataSource" ref="basicDataSource" />
          <property name="persistenceUnitName" value="myJpaPersistenceUnit" />
          <property name="packagesToScan">
             <list>
                <value>com.concretepage.entity</value>
             </list>
          </property>         
	  <property name="jpaProperties">
	     <props>
	        <prop key="hibernate.dialect">${hibernate.dialect}</prop>
	        <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
	        <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>			   
	     </props>
	  </property>
	</bean>
	<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
	    <property name="entityManagerFactory" ref="entityManagerFactoryBean" />
	</bean>
	<bean class="com.concretepage.dao.UserDAO"/>
	<tx:annotation-driven transaction-manager="txManager" /> 		
</beans>  
Find the description about configuration.
1. First of all we have created a bean for data source using following class.
org.apache.commons.dbcp2.BasicDataSource 
BasicDataSource is used to configure database driver name, URL, username and password.
2. Now we are creating a JPA vendor adapter bean using HibernateJpaVendorAdapter class. So JPA will use hibernate persistence provider.
3. To get the entity manager factory we need to create a bean for following class.
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean 
This gives us EntityManagerFactory and then we get EntityManager from this factory in our DAO using dependency injection to interact with database. Like JavaConfig for JPA, here we will configure properties such as jpaVendorAdapter, dataSource, persistenceUnitName, packagesToScan and jpaProperties.
4. For transaction management we have configured JpaTransactionManager and we have enabled annotation-driven transaction management.

3. Spring Security XML Configuration

Find the security configuration using XML.
security-config.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.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security.xsd">
	<http>
		<intercept-url  pattern="/user/**" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')" />
		<form-login 
		   login-page="/customLogin.jsp" 
		   login-processing-url="/appLogin"
		   username-parameter="app_username"
		   password-parameter="app_password"
		   default-target-url="/user/home"/>
		<logout 
		   logout-url="/appLogout" 
		   logout-success-url="/customLogin.jsp"/>  
		<access-denied-handler error-page="/user/error"/>
	</http>
	<beans:bean name="bcryptEncoder"
	      class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>	
	<beans:bean name="myAppUserDetailsService"
	      class="com.concretepage.service.MyAppUserDetailsService"/>
	<beans:bean name="userService"
	      class="com.concretepage.service.UserService"/>	      
	<authentication-manager>
		<authentication-provider user-service-ref="myAppUserDetailsService">
		    <password-encoder ref="bcryptEncoder"/>
		</authentication-provider>
	</authentication-manager>
	<global-method-security secured-annotations="enabled" />
</beans:beans>  
Let us discuss the configuration.
1. <http> element is a container element for HTTP security configuration. By default CSRF protection is enabled. It uses following sub-elements.
<intercept-url>: Defines the URL patterns that can be accessed by specified user roles.
<form-login>: Configures the custom login page. Here we can also change username and password parameter used in login page and other spring security configuration.
<logout>: It configures logout URL and logout success URL.
<access-denied-handler>: If user gets access denied error, the specified error page will be displayed.
2. <authentication-manager> element registers AuthenticationManager. Within this element we need to configure <authentication-provider> and this elements configures instance of spring UserDetailsService.
3. As we are using BCrypt password encoding so we need to configure bean for spring BCryptPasswordEncoder class and register with authentication provider.
4. <global-method-security> element enables method level security for those bean which are configured here in XML configuration. Here we have enabled @Secured annotation.

4. Spring MVC XML Configuration

Find the spring MVC XML configuration used in our example.
dispatcher-servlet.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:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
        
	<context:component-scan base-package="com.concretepage" />
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	  <property name="prefix" value="/WEB-INF/secure/"/>
	  <property name="suffix" value=".jsp"/> 
        </bean>
</beans> 
Find the configuration details.
1. <context:component-scan> element is used to specify base package name. The base package and its sub-package classes can be scanned that are annotated with spring stereotype annotation such as @Service, @Controller etc.
2. Create a bean for InternalResourceViewResolver and configure prefix and suffix that will be added to create URL for a view file kept in WEB-INF folder.

5. web.xml

Find web.xml used in our example.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

	<display-name>Spring 4 Security Example</display-name>
	<context-param>
	    <param-name>contextConfigLocation</param-name>
	    <param-value>
	       /WEB-INF/dispatcher-servlet.xml
	       /WEB-INF/security-config.xml
	       /WEB-INF/db-config.xml
	    </param-value>		    
	</context-param>	
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>
		    org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- Spring Security Configuration -->
	<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>  
Following configurations are taking place.
1. Configure all spring context files using <context-param> with parameter name contextConfigLocation.
2. Configure DispatcherServlet that handles all HTTP requests.
3. ContextLoaderListener listens to all start up and shutdown web application context of spring root.
4. DelegatingFilterProxy is a proxy for standard servlet filter delegating to a spring-managed bean that implements the Filter interface.

Run Application

To build and run the demo application, follow the steps.
1. Import database in MySQL given above on this page.
2. Download the source code from the link given below on this page.
3. Go to the root directory of the project using command prompt.
4. Build the project using gradle with following command.
gradle clean build 
We can find WAR file in the build\libs directory. If we want to build the project using maven, use following command.
mvn clean package 
We can find WAR file in the target directory.
5. Deploy the WAR file using tomcat and access the URL as given below.
http://localhost:8080/spring-app/user/home 
A login page will open. Find the print screen.
Spring 4 Security + JPA 2 + Spring 4 MVC + Hibernate + MySQL using Annotation + XML Example
Enter a user credential with role ROLE_ADMIN such as mahesh/m123 or trump/t123. Suppose I am entering trump/t123 credential. User will be able to see profile. Find the print screen.
Spring 4 Security + JPA 2 + Spring 4 MVC + Hibernate + MySQL using Annotation + XML Example
If we enter a user credential with role ROLE_USER then that user will not be successful to see profile because in the service layer the method getDataByUserName() from the IUserService.java interface is secured and authorized only to ROLE_ADMIN. For the demo we have a user arvind/a123 with ROLE_USER in our database. Now login using this credential. Find the print screen of the output.
Spring 4 Security + JPA 2 + Spring 4 MVC + Hibernate + MySQL using Annotation + XML Example


I am done now. Happy Spring Security learning!

Reference

Spring Security Reference

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us