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
, HibernateJpaAutoConfiguration
package 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