JavaRush /Курси /Модуль 5. Spring /Збереження користувачів і ролей у базі даних

Збереження користувачів і ролей у базі даних

Модуль 5. Spring
Рівень 17 , Лекція 6
Відкрита

У реальних додатках дані про користувачів і їхні ролі (а також дозволи) рідко зберігаються в статичних конфігураційних файлах. База даних робить керування користувачами більш гнучким і зручним. Наприклад:

  1. Динамічне керування користувачами — можливість додавання й видалення користувачів "на льоту".
  2. Безпека — паролі користувачів можуть бути надійно зашифровані прямо в базі даних.
  3. Масштабованість — підтримка великої кількості користувачів.
  4. Інтеграція — легко синхронізувати користувачів з інших систем (наприклад, CRM).

Що будемо робити

A ось що:

  1. Створимо таблиці для зберігання даних користувачів і ролей.
  2. Налаштуємо зв'язок між таблицями (many-to-many).
  3. Створимо сутності користувача (User) і ролі (Role).
  4. Реалізуємо роботу з базою даних через репозиторії Spring Data JPA.
  5. Налаштуємо 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!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ