Spring MVC 4 Security + Hibernate 5 + MySQL using Annotation + XML Example
February 16, 2017
This page will walk through spring MVC 4 security + hibernate 5 + MySQL using annotation + XML example. Spring security has the UserDetailsService
interface that loads user from the given source. We can access user profile using hibernate as usual as we do in spring hibernate integration. The user profile with username, password and roles will be used by loadUserByUsername()
method in the UserDetailsService
implementation class. In our demo application we will use MySQL database. We will create two separate application, one using JavaConfig and second using XML configuration. In our JavaConfig based application, we will create separate JavaConfig for spring MVC, spring security and for spring database integration using hibernate and in this way we will get better configuration understanding. In XML configuration based application again we will create separate XML files for spring MVC, spring security and for spring database integration using hibernate. For authentication we will create a custom login page. Our JSP files will be using CSRF protection. In service layer we will create a secured method. To populate data from database we have DAO layer. In our application we will use HibernateTemplate
to query database. We will handle access denied exception and redirect on an error page. LocalSessionFactoryBean
will be used to create session factory. For data source we will use BasicDataSource
of commons.dbcp2 library. For password encoding we will use BCrypt password encoding. Now find the complete example step by step.
Contents
- Software Used
- Gradle and Maven to Build the Project
- MySQL Database and Java Entity for Hibernate
- Spring MVC 4 Security + Hibernate 5 + MySQL using Annotation
- 1. Project Structure using JavaConfig in Eclipse
- 2. Spring MVC Configuration
- 3. Spring Security Configuration
- 4. Database Configuration
- 5. User Authentication using Spring UserDetailsService
- 6. Create DAO and Service Class
- 7. Spring MVC Controller
- 8. Create Views
- Spring MVC 4 Security + Hibernate 5 + MySQL using XML Configuration
- 1. Project Structure using XML Configuration in Eclipse
- 2. Spring MVC XML Configuration
- 3. Spring Security XML Configuration
- 4. Database XML Configuration
- 5. web.xml
- Run Application
- Reference
- Download Source Code
Software Used
We are using following software in our example.1. Java 8
2. Spring 4.3
3. Hibernate 5
4. Gradle 3.3
5. Maven 3.3
6. Tomcat 8.0
7. MySQL 5.5
8. 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 'org.hibernate:hibernate-core:5.2.6.Final' 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>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.6.Final</version> </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 Java Entity for Hibernate
We are using MySQL database in our demo application. We will create a table for user profile that will contain columns such as user name, password, role etc. 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. Find the database schema.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 MVC 4 Security + Hibernate 5 + MySQL using Annotation
Here we will discuss the steps to create spring MVC 4 security + hibernate 5 + MySQL using annotation. We will create separate JavaConfig files for spring MVC, spring security and spring database configuration using hibernate. We have service layer, DAO layer and controller. For login, we will create a custom login page. Let us start step by step.1. Project Structure using JavaConfig in Eclipse
Find the project structure using JavaConfig in eclipse.2. Spring MVC Configuration
Find the Spring MVC configuration file. We are creating a bean ofInternalResourceViewResolver
that will help controller to access file which are put inside WEB-INF
folder. Find the configuration file.
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; } }
WEB-INF
because from this location it cannot be directly accessed by using URL on browser. These files can only be accessed by controllers using InternalResourceViewResolver
. It has methods such as setPrefix()
and setSuffix()
. setPrefix()
sets the prefix and setSuffix()
sets the suffix the view name to form a URL.
@EnableWebMvc
is annotated at class level with @Configuration
annotation. @EnableWebMvc
imports spring MVC configuration from WebMvcConfigurationSupport
.
@ComponentScan
annotation is used to configure component scanning directive. Here we have specified the directive as com.concretepage
. All the components within this package and its child packages will be scanned.
Now we need to register
DispatcherServlet
. When we are using JavaConfig then preferred way to register DispatcherServlet
is as follows.
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[]{ "/" }; } }
getRootConfigClasses()
.
3. Spring Security Configuration
For spring security configuration find the following JavaConfig.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; } }
@EnableWebSecurity
that helps to configure spring security from WebSecurityConfigurer
class. To override any security method we need to extend WebSecurityConfigurerAdapter
class.
We have overridden configure()
method from WebSecurityConfigurerAdapter
class. configure()
method is used to configure HttpSecurity
class. Within this method we are performing spring security login, logout and exception handling configuration.
In spring security using JavaConfig, CSRF protection is enabled by default. CSRF protection is Cross Site Request Forgery. If we want to disable it we can do it using
http.csrf().disable()
.
To authenticate user, we will use custom
UserDetailsService
which in turn will use hibernate ORM to interact with database. Using configureGlobal()
we will configure our custom UserDetailsService
instance. For this purpose spring security provides AuthenticationManagerBuilder
. Here we also configure password encoder.
In our example we are using
BCryptPasswordEncoder
for password encoding. It uses BCrypt strong hashing function. While instantiating BCryptPasswordEncoder
we can optionally pass strength or we can also pass SecureRandom
instance to its constructor. The default strength is 10.
In our example we are using
@Secured
annotation to secure service layer. This annotation will only be effective if we use @EnableGlobalMethodSecurity
with the attribute securedEnabled=true
annotated at class level with @Configuration
annotation.
Now we need to register
DelegatingFilterProxy
to use the springSecurityFilterChain before any other registered Filter
. We can do it as follows. Find the spring security initializer.
SecurityInitializer.java
package com.concretepage.config; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { }
ContextLoaderListener
that is responsible to startup and shut down spring root web application context.
4. Database Configuration
We will integrate hibernate with our spring MVC security using JavaConfig as follows.DBConfig.java
package com.concretepage.config; import java.io.IOException; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.SessionFactory; 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.hibernate5.HibernateTemplate; import org.springframework.orm.hibernate5.HibernateTransactionManager; import org.springframework.orm.hibernate5.LocalSessionFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableTransactionManagement @PropertySource("classpath:database.properties") public class DBConfig { @Autowired private Environment env; @Bean public HibernateTemplate hibernateTemplate() { return new HibernateTemplate(sessionFactory()); } @Bean public SessionFactory sessionFactory() { LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); lsfb.setDataSource(getDataSource()); lsfb.setPackagesToScan("com.concretepage.entity"); lsfb.setHibernateProperties(hibernateProperties()); try { lsfb.afterPropertiesSet(); } catch (IOException e) { e.printStackTrace(); } return lsfb.getObject(); } @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 HibernateTransactionManager hibernateTransactionManager(){ return new HibernateTransactionManager(sessionFactory()); } private Properties hibernateProperties() { 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; } }
@EnableTransactionManagement
that enables annotation-driven transaction management capability in spring. For hibernate transaction management, spring has HibernateTransactionManager
class. It allows one thread-bound session per factory. To enable HibernateTransactionManager
we have to create a bean of it in our configuration file.
To create a data source, we are using third party library commons-dbcp2. For basic implementation we are using
BasicDataSource
class of commons-dbcp2. We need to instantiate this class and set the database configuration related value such as driver class name, URL, username and password. In our example we are using MySQL database. To configure hibernate properties we have created a method that returns java Properties
loaded with hibernate properties such as hibernate.dialect, hibernate.show_sql etc.
For hibernate
SessionFactory
we need to instantiate spring LocalSessionFactoryBean
. Using LocalSessionFactoryBean
we configure data source, packages to scan entity and hibernate properties.
Create a bean of
HibernateTemplate
. This is a helper class that is used to interact with database. It automatically converts hibernate exceptions to data access exceptions. In DAO class we will use HibernateTemplate
methods to get data from database.
@PropertySource
annotation accesses the property file and data is loaded to Environment
and then by calling its method getProperty()
we fetch properties defined in property file. Now find the property file used in our example.
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
5. User Authentication using Spring UserDetailsService
Spring hasUserDetailsService
interface that loads user specific data. We need to create a class and implement UserDetailsService
for user authentication. It has a method loadUserByUsername()
that accepts user name and returns
org.springframework.security.core.userdetails.UserDetails
UserDetailsService
.
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; } }
6. Create DAO and Service Class
Here we are creating a DAO method that will return user profile for a given user name. Here we are usinghibernateTemplate.find()
method that executes an HQL query. Find the DAO class.
UserDAO.java
package com.concretepage.dao; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate5.HibernateTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.concretepage.entity.UserInfo; @Repository @Transactional public class UserDAO { @Autowired private HibernateTemplate hibernateTemplate; public UserInfo getActiveUser(String userName) { UserInfo activeUserInfo = new UserInfo(); short enabled = 1; List<?> list = hibernateTemplate.find("FROM UserInfo WHERE userName=? and enabled=?", userName, enabled); if(!list.isEmpty()) { activeUserInfo = (UserInfo)list.get(0); } return activeUserInfo; } }
getActiveUser()
method is used by MyAppUserDetailsService
to load the user for authentication and authorization.
@Transactional
annotation is used 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)
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); }
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); } }
@Secured
annotation. The service layer method getDataByUserName()
can be accessed by an authenticated user with ROLE_ADMIN . @Secured
annotation is effective only if we use below annotation in our spring security configuration class.
@EnableGlobalMethodSecurity(securedEnabled=true)
@Secured ({"ROLE_ADMIN", "ROLE_USER"}) UserInfo getDataByUserName(String userName);
7. Spring MVC Controller
Find the MVC controller used in our example.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"; } }
home()
will be called after successful authentication and the method error()
will be called when an authenticated user tries to access service layer secured method and throws access denied exception.
8. Create Views
Find the spring security login page. We have created a custom login page with 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>
After successful login, a user will access service layer. If the user role is authorized to access service layer, then user will get page which will display user information. Find the JSP page that will be used to display user information.
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 MVC 4 Security + Hibernate 5 + MySQL using XML Configuration
Here we will discuss the steps to create spring MVC 4 security + hibernate 5 + MySQL using XML configuration. We will create separate XML files for spring MVC, spring security and spring database configuration using hibernate. Service layer, DAO layer, controller and views will be same as given in the application using annotation. Let us start now to understand XML configuration.1. Project Structure using XML Configuration in Eclipse
Find the project structure using XML configuration in eclipse.2. Spring MVC XML Configuration
Find the spring MVC XML configuration.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>
<context:component-scan>
plays the role to scan components. We need to specify base package. All the components of this package and sub packages will be scanned. Annotation configuration will be true by default.
To access views which lie within the folder WEB-INF, we need to configure
InternalResourceViewResolver
. Specify prefix and suffix that will be used to create URL for views. To keep view files within WEB-INF is good practice because it cannot be accessed through URL directly from browser but it can be accessed only by controller classes.
3. Spring Security XML Configuration
Find the XML based security configuration.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>
<http>
is the container element for HTTP security configuration. More than one <http>
element can be defined with a specific pattern. We can use auto-config="true"
for default login and logout. If we want to customize it then we should use <form-login>
and <logout>
sub element within <http>
element. To handle access denied exception for a secured service layer, we should use <access-denied-handler>
element to configure an error page. Spring security version 4 onwards, CSRF protection is enabled by default in XML configuration. We can disable it by using sub element <csrf disabled="true"/>
within <http>
element.
<authentication-manager>
element registers spring AuthenticationManager
and <authentication-provider>
element specifies user service used in our spring security.
<global-method-security>
element plays the role to secure methods for all the beans which are registered in our spring application context. The attribute secured-annotations="enabled"
enables the @Secured
annotation used with the methods.
4. Database XML Configuration
Find the database configuration for hibernate using XML namespace.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="dataSource" 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 id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="hibernateProperties"> <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> <property name="packagesToScan" value="com.concretepage.entity"></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"> <constructor-arg name="sessionFactory" ref="sessionFactory"/> </bean> <bean class="com.concretepage.dao.UserDAO" /> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
BasicDataSource
of commons.dbcp2
library. <context:property-placeholder>
is used to read the database properties from a property file. Create a session factory with LocalSessionFactoryBean
using data source and hibernate properties. Using session factory create a bean for transaction manager with HibernateTransactionManager
. Using session factory we will also create a bean for HibernateTemplate
that will be used to query the database. Now configure transaction manager for annotation driven transaction management.
5. web.xml
Find theweb.xml
for spring MVC security.
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>
DispatcherServlet
works as central dispatcher for HTTP request controllers. It dispatches the HTTP request to registered handler for processing it, mapping URLs and exception handlings. Now configure ContextLoaderListener
to listen startup and shutdown spring root web application context. We will also configure DelegatingFilterProxy
filter that works as a proxy for standard servlet filter delegating to spring-managed bean. Now all calls to filter will be delegated to that bean in spring context.
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!