Create Spring MVC Project
On the Eclipse, create a Spring MVC project in Spring Boot
Enter Project Information:
- Name: LearnSpringMVCWithRealApps
- Group: com.demo
- Artifact: LearnSpringMVCWithRealApps
- Description: Learn Spring MVC with Real Apps
- Package: com.demo
Select the technologies and libraries to be used:
- Web
- Security
- MySQL
- JPA
Click Next button to show Site Information for project
Click Finish button to finish create Spring MVC project
Configure 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.demo</groupId>
<artifactId>LearnSpringMVCWithRealApps</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>LearnSpringMVCWithRealApps</name>
<description>Learn Spring MVC with Real Apps</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSTL tag lib -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>javax.servlet.jsp.jstl-api</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Tomcat for JSP rendering -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Create Database
Create a database with the name is learn_spring_mvc_with_real_apps. This database have 3 tables: Roles, Users and User_Role
--
-- Table structure for table `roles`
--
CREATE TABLE `roles` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` varchar(250) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Dumping data for table `roles`
--
INSERT INTO `roles` (`name`) VALUES
(1, 'ROLE_SUPER_ADMIN'),
(2, 'ROLE_ADMIN'),
(3, 'ROLE_EMPLOYEE');
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`username` varchar(100) NOT NULL,
`password` varchar(300) NOT NULL,
`enabled` int(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`id`, `username`, `password`, `enabled`) VALUES
(1, 'acc1', '$2a$04$YnS.4Ak1DPOV2YDjzPSl.ugIVk4/gXGPAHtSd/8g3fj2vJOuV49AG', 1),
(2, 'acc2', '$2a$04$YnS.4Ak1DPOV2YDjzPSl.ugIVk4/gXGPAHtSd/8g3fj2vJOuV49AG', 1),
(3, 'acc3', '$2a$04$YnS.4Ak1DPOV2YDjzPSl.ugIVk4/gXGPAHtSd/8g3fj2vJOuV49AG', 1);
-- --------------------------------------------------------
--
-- Table structure for table `user_role`
--
CREATE TABLE `user_role` (
`userid` int(11) NOT NULL PRIMARY KEY,
`roleid` int(11) NOT NULL PRIMARY KEY,
`enable` tinyint(1) NOT NULL,
FOREIGN KEY (`userid`) REFERENCES `users` (`id`),
FOREIGN KEY (`roleid`) REFERENCES `roles` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `user_role`
--
INSERT INTO `user_role` (`userid`, `roleid`, `enable`) VALUES
(1, 1, 1),
(1, 2, 1),
(1, 3, 1),
(2, 2, 1),
(2, 3, 1),
(3, 3, 1);
Structure of Roles Table
Data of Roles Table
Structure of Users Table
Data of Users Table
Structure of User_Role Table
Data of User_Role Table
Configure application.properties
spring.mvc.view.prefix = /WEB-INF/views/
spring.mvc.view.suffix = .jsp
spring.mvc.static-path-pattern=/resources/**
spring.datasource.url= jdbc:mysql://localhost:3306/learn_spring_mvc_with_real_apps
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
server.port=9596
Entities Class
Create new package named com.demo.entities. In this package, create new entities as below:
Role Entity
Create new java class named Role.java as below:
package com.demo.entities;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "roles")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToMany(mappedBy = "role")
private List<UserRole> userRoles;
public Role() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public List<UserRole> getUserRoles() {
return this.userRoles;
}
public void setUserRoles(List<UserRole> userRoles) {
this.userRoles = userRoles;
}
public UserRole addUserRole(UserRole userRole) {
getUserRoles().add(userRole);
userRole.setRole(this);
return userRole;
}
public UserRole removeUserRole(UserRole userRole) {
getUserRoles().remove(userRole);
userRole.setRole(null);
return userRole;
}
}
User Entity
Create new java class named User.java as below:
package com.demo.entities;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private boolean enabled;
private String password;
private String username;
@OneToMany(mappedBy = "user")
private List<UserRole> userRoles;
public User() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public List<UserRole> getUserRoles() {
return this.userRoles;
}
public void setUserRoles(List<UserRole> userRoles) {
this.userRoles = userRoles;
}
public UserRole addUserRole(UserRole userRole) {
getUserRoles().add(userRole);
userRole.setUser(this);
return userRole;
}
public UserRole removeUserRole(UserRole userRole) {
getUserRoles().remove(userRole);
userRole.setUser(null);
return userRole;
}
}
UserRole Entity
Create new java class named UserRole.java as below:
package com.demo.entities;
import java.io.Serializable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "user_role")
public class UserRole implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private UserRolePK id;
private boolean enable;
@ManyToOne
@JoinColumn(name = "userid", insertable = false, updatable = false)
private User user;
@ManyToOne
@JoinColumn(name = "roleid", insertable = false, updatable = false)
private Role role;
public UserRole() {
}
public UserRolePK getId() {
return this.id;
}
public void setId(UserRolePK id) {
this.id = id;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
}
UserRolePK Entity
Create new java class named UserRolePK.java as below:
package com.demo.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class UserRolePK implements Serializable {
private static final long serialVersionUID = 1L;
@Column(insertable = false, updatable = false)
private int userid;
@Column(insertable = false, updatable = false)
private int roleid;
public UserRolePK() {
}
public int getUserid() {
return this.userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public int getRoleid() {
return this.roleid;
}
public void setRoleid(int roleid) {
this.roleid = roleid;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof UserRolePK)) {
return false;
}
UserRolePK castOther = (UserRolePK) other;
return (this.userid == castOther.userid) && (this.roleid == castOther.roleid);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + this.userid;
hash = hash * prime + this.roleid;
return hash;
}
}
Repositories
Create new package named com.demo.repositories. In this package, create new repositories use Spring Data JPA as below:
UserRepository Interface
Create new interface named UserRepository.java as below:
package com.demo.repositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.demo.entities.User;
@Repository("userRepository")
public interface UserRepository extends CrudRepository<User, Integer> {
public User findByUsername(String username);
}
Services Class
Create new package named com.demo.services. In this package, create new services as below:
UserService Interface
Create new interface named UserService.java as below:
package com.demo.services;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface UserService extends UserDetailsService {
}
UserServiceImpl Class
Create new class named UserServiceImpl.java as below:
package com.demo.services;
import java.util.ArrayList;
import java.util.List;
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.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.demo.entities.User;
import com.demo.entities.UserRole;
import com.demo.repositories.UserRepository;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Username not found for " + username);
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (UserRole userRole : user.getUserRoles()) {
grantedAuthorities.add(new SimpleGrantedAuthority(userRole.getRole().getName()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}
Controllers
Create new package named com.demo.controllers. In this package, create new controllers as below:
Home Controller
Create new controller named HomeController.java as below:
package com.demo.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("home")
public class HomeController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "home/index";
}
}
SuperAdmin Controller
Create new controller named SuperAdminController.java as below:
package com.demo.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/superadmin**")
public class SuperAdminController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "superadmin/index";
}
}
Admin Controller
Create new controller named AdminController.java as below:
package com.demo.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/admin**")
public class AdminController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "admin/index";
}
}
Employee Controller
Create new controller named EmployeeController.java as below:
package com.demo.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/employee**")
public class EmployeeController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "employee/index";
}
}
DashBoard Controller
Create new controller named DashBoardController.java as below:
package com.demo.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/dashboard")
public class DashBoardController {
@RequestMapping(method = RequestMethod.GET)
public String index() {
return "redirect:/dashboard/login";
}
@RequestMapping(value = "login", method = RequestMethod.GET)
public String login(
@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout,
ModelMap modelMap) {
if(error != null) {
modelMap.put("msg", "Invalid username or password!");
}
if(logout != null) {
modelMap.put("msg", "Logout successfully");
}
return "dashboard/login";
}
@RequestMapping(value = "welcome", method = RequestMethod.GET)
public String welcome() {
return "dashboard/welcome";
}
@RequestMapping(value = "accessDenied", method = RequestMethod.GET)
public String accessDenied() {
return "dashboard/accessDenied";
}
}
Create View
Create new folders with path webapp\WEB-INF\views in src\main. In views folder, create new JSP Pages as below:
Home View
Create new folder named home. Create new jsp file named index.jsp as below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Home Page</title>
</head>
<body>
<h3>Home Page</h3>
</body>
</html>
SuperAdmin View
Create new folder named superadmin. Create new jsp file named index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Super Admin Page</title>
</head>
<body>
<h3>Super Admin Page</h3>
Welcome ${pageContext.request.userPrincipal.name}
<br>
<a href="${pageContext.request.contextPath }/dashboard/logout">Logout</a>
</body>
</html>
Admin View
Create new folder named admin. Create new jsp file named index.jsp as below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Admin Page</title>
</head>
<body>
<h3>Admin Page</h3>
Welcome ${pageContext.request.userPrincipal.name}
<br>
<a href="${pageContext.request.contextPath }/dashboard/logout">Logout</a>
</body>
</html>
Employee View
Create new folder named employee. Create new jsp file named index.jsp as below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Employee Page</title>
</head>
<body>
<h3>Employee Page</h3>
Welcome ${pageContext.request.userPrincipal.name}
<br>
<a href="${pageContext.request.contextPath }/dashboard/logout">Logout</a>
</body>
</html>
DashBoard View
Create new folder named dashboard. In this folder, create new views as below:
Login View
Create new jsp file named login.jsp as below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Page</title>
</head>
<body>
<h3>Login Page</h3>
${msg }
<form method="post"
action="${pageContext.request.contextPath }/dashboard/process-login">
<table cellpadding="2" cellspacing="2">
<tr>
<td>Username</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Login"></td>
</tr>
</table>
</form>
</body>
</html>
Welcome View
Create new jsp file named welcome.jsp as below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome Page</title>
</head>
<body>
<h3>Welcome Page</h3>
Welcome ${pageContext.request.userPrincipal.name}
<br>
<a href="${pageContext.request.contextPath }/dashboard/logout">Logout</a>
</body>
</html>
AccessDenied View
Create new jsp file named accessDenied.jsp as below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Access Denied Page</title>
</head>
<body>
<h3>Access Denied Page</h3>
<a href="${pageContext.request.contextPath}/dashboard/welcome">Back</a>
</body>
</html>
Security Configuration
In com.demo package, create new java class named SecurityConfiguration.java use for Spring Security as below:
package com.demo;
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.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 com.demo.services.UserService;
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and().csrf().disable();
httpSecurity.authorizeRequests()
.antMatchers("/superadmin**").access("hasRole('ROLE_SUPER_ADMIN')")
.antMatchers("/admin**").access("hasRole('ROLE_SUPER_ADMIN') or hasRole('ROLE_ADMIN')")
.antMatchers("/employee**").access("hasRole('ROLE_SUPER_ADMIN') or hasRole('ROLE_ADMIN') or hasRole('ROLE_EMPLOYEE')")
.and()
.formLogin().loginPage("/dashboard/login")
.loginProcessingUrl("/dashboard/process-login")
.defaultSuccessUrl("/dashboard/welcome")
.failureUrl("/dashboard/login?error")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutUrl("/dashboard/logout")
.logoutSuccessUrl("/dashboard/login?logout")
.and()
.exceptionHandling().accessDeniedPage("/dashboard/accessDenied");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService);
}
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
Structure of Project
Run Application
Select LearnSpringMVCWithRealAppsApplication.java file in com.demo package, right click and select Run As/Spring Boot App menu
Access index method in home controller with following url: http://localhost:9596/home
Output
Test access index action in superadmin controller without account with url: http://localhost:9596/superadmin
Output
Test access index action in admin controller without account with url: http://localhost:9596/admin
Output
Test access index action in employee controller without account with url: http://localhost:9596/employee
Output
Test login with invalid account: username is abc and password is 123
Output
Test login with valid account: username is acc2 and password is 123. This account have roles: ROLE_ADMIN and ROLE_EMPLOYEE
Output
Use acc2 has logged access index action in superadmin controller with url: http://localhost:9596/superadmin
Output
Use acc2 has logged access index action in admin controller with url: http://localhost:9596/admin
Output
Use acc2 has logged access index action in employee controller with url: http://localhost:9596/employee
Output
References
I recommend you refer to the books below to learn more about the knowledge in this article:
- Spring MVC Beginners Guide – Second Edition
- Spring Boot in Action
- Pro Spring Boot
- Spring Data
- Spring Security – Third Edition: Secure your web applications, RESTful services, and microservice architectures
- Spring Security Essentials
- OAuth 2.0 Cookbook: Protect your web applications using Spring Security
- Pro Spring Security