Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven
(Xem lại: Code ví dụ Spring Boot Security Hello + Tạo Form Login)
(Xem lại: Code ví dụ Spring Boot với Hibernate + MySQL + Maven + Eclipse)
Các công nghệ sử dụng:
- Spring Boot 2.0.1
- Maven
- JDK 1.8
- Eclipse + Spring Tool Suite
- MySQL
Tạo Database
Tạo database “spring_security” spring_security.sql
Tạo table users chứa thông tin username, password và enabled (enabled = 1 tức là account đã được active)
CREATE TABLE `spring-security`.`users` ( `id` INT NOT NULL AUTO_INCREMENT, `username` VARCHAR(45) NULL, `password` VARCHAR(255) NULL, `enabled` INT(1) NULL, PRIMARY KEY (`id`), UNIQUE INDEX `username_UNIQUE` (`username` ASC));
Tạo table role chứa thông tin các role.
CREATE TABLE `spring_security`.`role` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(45) NULL, PRIMARY KEY (`id`));
Tạo table users_roles thực hiện mapping để biết user nào có những role nào
CREATE TABLE `spring_security`.`users_roles` (
`id` INT NOT NULL AUTO_INCREMENT,
`user` INT NULL,
`role` INT NULL,
PRIMARY KEY (`id`),
INDEX `_idx` (`user` ASC),
INDEX `dfdf_idx` (`role` ASC),
CONSTRAINT `user`
FOREIGN KEY (`user`)
REFERENCES `spring-security`.`users` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `role`
FOREIGN KEY (`role`)
REFERENCES `spring-security`.`role` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
Tạo 2 tài khoản kai/123456 và sena/123456 (ở đây mình mã hóa mật khẩu bằng bcypt)
INSERT INTO `users` VALUES (1,'kai','$2a$04$GYGsaJj9l6kH2GikK6QVzO0v3sOCxt3vdkiA2/tcoSw8erI85ZYDG',1),(2,'sena','$2a$04$GYGsaJj9l6kH2GikK6QVzO0v3sOCxt3vdkiA2/tcoSw8erI85ZYDG',1);
Tạo 2 role là ROLE_ADMIN và ROLE_USER
INSERT INTO `role` VALUES (1,'ROLE_ADMIN'),(2,'ROLE_USER');
Thiết lập role = “ROLE_ADMIN”, “ROLE_USER” cho tài khoản ‘kai’, và role = “ROLE_USER” cho tài khoản ‘sena’
INSERT INTO `users_roles` VALUES (1,1,1),(2,1,2),(4,2,2);
Tạo Spring Boot Project
Cấu trúc project
Cấu hình Hibernate
application.properties.# =============Database config================== spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/spring-boot-hibernate spring.datasource.username=root spring.datasource.password=admin1234 # ==============JPA / HIBERNATE================= spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
DataSourceAutoConfiguration, DataSourceTransactionManagerAutoConfiguration, HibernateJpaAutoConfigurationpackage stackjava.com.sbsecurityhibernate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
@SpringBootApplication
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class SpringBootSecurityHibernateApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSecurityHibernateApplication.class, args);
}
}
Tạo các bean cấu hình cho Hibernate
package stackjava.com.sbsecurityhibernate.config;
import java.util.Properties;
import javax.sql.DataSource;
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.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
@Configuration
public class HibernateConfig {
@Autowired
private Environment env;
@Bean(name = "dataSource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
@Autowired
@Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) throws Exception {
Properties properties = new Properties();
properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
properties.put("current_session_context_class", //
env.getProperty("spring.jpa.properties.hibernate.current_session_context_class"));
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
// Package contain entity classes
factoryBean.setPackagesToScan("stackjava.com.sbsecurityhibernate.entities");
factoryBean.setDataSource(dataSource);
factoryBean.setHibernateProperties(properties);
factoryBean.afterPropertiesSet();
//
SessionFactory sf = factoryBean.getObject();
System.out.println("## getSessionFactory: " + sf);
return sf;
}
@Autowired
@Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
}
Cấu hình Spring Security
package stackjava.com.sbsecurityhibernate.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.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 stackjava.com.sbsecurityhibernate.service.MyUserDetailsService;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// Sét đặt dịch vụ để tìm kiếm User trong Database.
// Và sét đặt PasswordEncoder.
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Chỉ cho phép user có quyền ADMIN truy cập đường dẫn /admin/**
http.authorizeRequests().antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')");
// Chỉ cho phép user có quyền ADMIN hoặc USER truy cập đường dẫn
// /user/**
http.authorizeRequests().antMatchers("/user/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')");
// Khi người dùng đã login, với vai trò USER, Nhưng truy cập vào trang
// yêu cầu vai trò ADMIN, sẽ chuyển hướng tới trang /403
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");
// Cấu hình cho Login Form.
http.authorizeRequests().and().formLogin()//
.loginProcessingUrl("/j_spring_security_login")//
.loginPage("/login")//
.defaultSuccessUrl("/user")//
.failureUrl("/login?message=error")//
.usernameParameter("username")//
.passwordParameter("password")
// Cấu hình cho Logout Page.
.and().logout().logoutUrl("/j_spring_security_logout").logoutSuccessUrl("/login?message=logout");
}
}
File Controller
package stackjava.com.sbsecurityhibernate.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class BaseController {
@RequestMapping(value = { "/", "/login" })
public String login(@RequestParam(required = false) String message, final Model model) {
if (message != null && !message.isEmpty()) {
if (message.equals("logout")) {
model.addAttribute("message", "Logout!");
}
if (message.equals("error")) {
model.addAttribute("message", "Login Failed!");
}
}
return "login";
}
@RequestMapping("/user")
public String user() {
return "user";
}
@RequestMapping("/admin")
public String admin() {
return "admin";
}
@RequestMapping("/403")
public String accessDenied() {
return "403";
}
}
Các file entities:
package stackjava.com.sbsecurityhibernate.entities;
import static javax.persistence.GenerationType.IDENTITY;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@Entity
@Table(name = "users", catalog = "spring_security", uniqueConstraints = @UniqueConstraint(columnNames = "username"))
public class User implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Set<UsersRoles> usersRoleses = new HashSet<UsersRoles>(0);
public User() {
}
public User(final String username, final String password, final Boolean enabled,
final Set<UsersRoles> usersRoleses) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.usersRoleses = usersRoleses;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(final Integer id) {
this.id = id;
}
@Column(name = "username", unique = true, length = 45)
public String getUsername() {
return this.username;
}
public void setUsername(final String username) {
this.username = username;
}
@Column(name = "password")
public String getPassword() {
return this.password;
}
public void setPassword(final String password) {
this.password = password;
}
@Column(name = "enabled", nullable = false, columnDefinition = "TINYINT(1)")
public Boolean getEnabled() {
return this.enabled;
}
public void setEnabled(final Boolean enabled) {
this.enabled = enabled;
}
@OneToMany(fetch = FetchType.EAGER, mappedBy = "users")
public Set<UsersRoles> getUsersRoleses() {
return this.usersRoleses;
}
public void setUsersRoleses(final Set<UsersRoles> usersRoleses) {
this.usersRoleses = usersRoleses;
}
@Transient
public List<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (UsersRoles usersRoles: this.usersRoleses) {
authorities.add(new SimpleGrantedAuthority(usersRoles.getRole().getName()));
}
return authorities;
}
}
package stackjava.com.sbsecurityhibernate.entities;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "role", catalog = "spring_security")
public class Role implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Set<UsersRoles> usersRoleses = new HashSet<UsersRoles>(0);
public Role() {
}
public Role(String name, Set<UsersRoles> usersRoleses) {
this.name = name;
this.usersRoleses = usersRoleses;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "name", length = 45)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "role")
public Set<UsersRoles> getUsersRoleses() {
return this.usersRoleses;
}
public void setUsersRoleses(Set<UsersRoles> usersRoleses) {
this.usersRoleses = usersRoleses;
}
}
package stackjava.com.sbsecurityhibernate.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "users_roles", catalog = "spring_security")
public class UsersRoles implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private Role role;
private User users;
public UsersRoles() {
}
public UsersRoles(Role role, User users) {
this.role = role;
this.users = users;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "role")
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user")
public User getUsers() {
return this.users;
}
public void setUsers(User users) {
this.users = users;
}
}
File DAO:
package stackjava.com.sbsecurityhibernate.dao;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import stackjava.com.sbsecurityhibernate.entities.User;
@Repository(value = "userDAO")
@Transactional(rollbackFor = Exception.class)
public class UserDAO {
@Autowired
private SessionFactory sessionFactory;
public User loadUserByUsername(final String username) {
List<User> users = new ArrayList<User>();
Session session = this.sessionFactory.getCurrentSession();
users = session.createQuery("from User where username=?", User.class).setParameter(0, username).list();
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
}
File Service:
package stackjava.com.sbsecurityhibernate.service;
import org.springframework.beans.factory.annotation.Autowired;
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 stackjava.com.sbsecurityhibernate.dao.UserDAO;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserDAO userDAO;
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
stackjava.com.sbsecurityhibernate.entities.User user = userDAO.loadUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Username Not Found");
}
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(username, user.getPassword(), enabled, accountNonExpired, credentialsNonExpired,
accountNonLocked, user.getAuthorities());
}
}
Các file views:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Security Hibernate</title>
</head>
<body>
<h2>Spring Boot Security Hibernate</h2>
<h3><p th:text="${message}"></p></h3>
<form name='login-form' th:action="@{/j_spring_security_login}" method='POST'>
<table>
<tr>
<td>Username:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security Hello</title>
</head>
<body>
<h2>User Page</h2>
<h3>
Welcome : <span th:utext="${#request.userPrincipal.name}"></span>
</h3>
<a th:href="@{/admin}">Admin Page</a>
<br/><br/>
<form th:action="@{/j_spring_security_logout}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="submit" value="Logout" />
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security Hello</title>
</head>
<body>
<h2>Admin Page</h2>
<h3>
Hello : <span th:utext="${#request.userPrincipal.name}"></span>
</h3>
<a th:href="@{/user}">User Page</a>
<br/><br/>
<form th:action="@{/j_spring_security_logout}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="submit" value="Logout" />
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security Hello</title>
</head>
<body>
<h2>Access Denied Page - 403</h2>
<h3>
Hi : <span th:utext="${#request.userPrincipal.name} + ' - you do not have permission to access this page'"></span>
</h3>
<a th:href="@{/user}">User Page</a>
<br/><br/>
<form th:action="@{/j_spring_security_logout}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="submit" value="Logout" />
</form>
</body>
</html>
Demo:
Đăng nhập với tài khoản kai/123456 – (role = ROLE_ADMIN)
Đăng nhập với tài khoản sena/123456 (role = ROLE_USER)
Tài khoản sena/123456 không có role ROLE_ADMIN nên sẽ bị từ chối truy cập trang /admin
Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven stackjava.com
Okay, Done!
Download code ví dụ trên tại đây.
References:
https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle
https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-data-access