Spring MVC Security Example
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.
Contents
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' }
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);
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")); } }
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.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; } }
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
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()
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
@PropertySource("classpath:database.properties")
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; } }
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 { }
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; } }
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[]{ "/" }; } }
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 providesUserDetailsService
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; } }
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 entityUserInfo
. 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; } }
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)
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); }
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"; } }
@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>
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>
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>
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 inweb.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.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>
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
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>
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>
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
Findweb.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>
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
mvn clean package
5. Deploy the WAR file using tomcat and access the URL as given below.
http://localhost:8080/spring-app/user/home
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.
I am done now. Happy Spring Security learning!