У реальних додатках дані про користувачів і їхні ролі (а також дозволи) рідко зберігаються в статичних конфігураційних файлах. База даних робить керування користувачами більш гнучким і зручним. Наприклад:
- Динамічне керування користувачами — можливість додавання й видалення користувачів "на льоту".
- Безпека — паролі користувачів можуть бути надійно зашифровані прямо в базі даних.
- Масштабованість — підтримка великої кількості користувачів.
- Інтеграція — легко синхронізувати користувачів з інших систем (наприклад, CRM).
Що будемо робити
A ось що:
- Створимо таблиці для зберігання даних користувачів і ролей.
- Налаштуємо зв'язок між таблицями (many-to-many).
- Створимо сутності користувача (
User) і ролі (Role). - Реалізуємо роботу з базою даних через репозиторії Spring Data JPA.
- Налаштуємо
UserDetailsServiceдля інтеграції бази даних з Spring Security.
Крок 1. Створення таблиць для користувачів і ролей
Почнемо з дизайну бази даних. Нам потрібні дві таблиці: одна для зберігання інформації про користувачів, інша — для ролей. Окрім цього, потрібно зв'язати їх через проміжну таблицю, щоб реалізувати зв'язок "багато до багатьох". Ось приклад SQL-запиту для створення таблиць:
CREATE TABLE role (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE user_roles (
user_id BIGINT,
role_id BIGINT,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES user(id),
CONSTRAINT fk_role FOREIGN KEY (role_id) REFERENCES role(id)
);
Що означають ці поля:
- Таблиця
user:username— ім'я користувача, використовується для входу в систему.password— зберігаємо зашифрований пароль (детальніше про це пізніше).enabled— прапорець, що позначає, чи активний користувач.
- Таблиця
role:- Кожна роль має унікальне ім'я (наприклад,
ROLE_USER,ROLE_ADMIN).
- Кожна роль має унікальне ім'я (наприклад,
- Таблиця
user_roles:- Представляє зв'язок між користувачами та їх ролями.
Крок 2. Створення сутностей User і Role
Спочатку створимо Java-класи, що представляють наші таблиці. Ми будемо використовувати JPA-аннотації для зіставлення полів сутностей з колонками в таблицях бази даних.
Клас Role
import jakarta.persistence.*;
import java.util.Set;
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String name;
public Role() {}
public Role(String name) {
this.name = name;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Клас User
import jakarta.persistence.*;
import java.util.Set;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
private boolean enabled = true;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles;
public User() {}
public User(String username, String password, Set<Role> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Крок 3. Репозиторії Spring Data JPA
Тепер створимо репозиторії для взаємодії з базою даних.
Інтерфейс RoleRepository
import org.springframework.data.jpa.repository.JpaRepository;
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String name);
}
Інтерфейс UserRepository
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
Ці репозиторії спростять виконання часто вживаних операцій: пошук користувача за іменем, додавання нових ролей тощо.
Крок 4. Налаштування UserDetailsService
Для інтеграції бази даних із Spring Security потрібно надати реалізацію інтерфейсу UserDetailsService. Це дозволить фреймворку підвантажувати інформацію про користувача під час аутентифікації.
Реалізація UserDetailsService
import org.springframework.security.core.userdetails.*;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
import java.util.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles().stream()
.map(Role::getName)
.collect(Collectors.toList()))
.build();
}
}
Крок 5. Додавання шифрування паролів
Шифрування паролів — обов'язковий етап. Використаємо BCryptPasswordEncoder:
import org.springframework.context.annotation.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Коли створюєте нових користувачів у базі даних, обов'язково шифруйте їхні паролі через PasswordEncoder.
Результати
Тепер ваш додаток готовий до безпечної роботи з користувачами та ролями з бази даних. Ви можете додавати, оновлювати та керувати ними, не вносячи змін у код. Такий підхід часто використовується в корпоративних системах, інтернет-магазинах та будь-яких інших системах, які потребують гнучкості в керуванні користувачами. Не забудьте нагадати користувачам вигадати дійсно складні паролі, щоб вони не стали жертвами password123!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ