Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven

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:

Tạo Database

Tạo database “spring_security”  spring_security.sql

Spring Security Hibernate database

 

spring security database login

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

Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven

Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven

Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven

Cấu trúc project

Code ví dụ Spring Boot Security Hibernate + MySQL + Eclipse + Maven

Cấu hình Hibernate

Cấu hình các thông số connect tới database và config hibernate trong file 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
Mặc định Spring Boot sẽ tự động cấu hình JPA và các bean liên quan gồm DataSourceAutoConfigurationDataSourceTransactionManagerAutoConfigurationHibernateJpaAutoConfiguration
Ở đây mình sử dụng Hibernate nên sẽ disable nó đi.
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)

Code ví dụ Spring Boot Security Hello + Tạo Form Login

Code ví dụ Spring Boot Security Hello + Tạo Form Login

Code ví dụ Spring Boot Security Hello + Tạo Form Login

Đăng nhập với tài khoản sena/123456 (role = ROLE_USER)

Code ví dụ Spring Boot Security Hello + Tạo Form Login

Code ví dụ Spring Boot Security Hello + Tạo Form Login

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 Hello + Tạo Form Login

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

stackjava.com