- •Цель работы
- •Теоретическая часть
- •Основные элементы Spring Security
- •Что произойдет после подключения Spring Security
- •Ход выполнения работы
- •Часть 0. Подготовка
- •Часть 1. Подключение Spring Security
- •Часть 2. Расширение модели пользователя
- •2.1. Создайте перечисление роли пользователя
- •2.2. Обновите сущность User
- •2.3. Обновите UserDto
- •Часть 3. Подготовка репозитория и загрузки пользователя
- •3.1. Обновите UserRepository
- •3.2. Создайте класс CustomUserDetails
- •3.3. Создайте класс CustomUserDetailsService
- •3.4. Почему это удобнее, чем возвращать встроенный User
- •Часть 4. Шифрование паролей и конфигурация безопасности
- •4.1. Создайте конфигурационный класс SecurityConfig
- •4.2. Что делает эта конфигурация
- •Часть 5. Регистрация пользователя
- •5.1. Создайте AuthService
- •5.2. Создайте AuthController
- •5.3. Проверьте регистрацию
- •Часть 6. Базовая аутентификация через HTTP Basic
- •6.1. Как это работает
- •6.2. Проверьте закрытые URL
- •Часть 7. Авторизация по ролям
- •7.1. Ограничение URL по ролям
- •7.2. Пример контроллера администратора
- •7.3. Методная авторизация
- •Часть 8. Как работают фильтры Spring Security
- •Часть 9. Сессии в Spring Security
- •9.1. Как работает сессия
- •9.2. Особенности сессий
- •9.3. Как это связано с нашим приложением
- •Часть 10. JWT: stateless-подход
- •10.1. Идея JWT
- •10.3. Логин с выдачей JWT
- •10.4. JWT-фильтр
- •10.5. Конфигурация для JWT
- •Часть 11. Сравнение сессий и JWT
- •Сессии
- •Часть 12. Проверка работы приложения
- •12.1. Проверка регистрации
- •12.2. Проверка Basic Auth
- •12.3. Проверка ролей
- •12.4. Проверка JWT
- •Самостоятельные задания
- •Контрольные вопросы
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("Пользователь не найден"));
return new CustomUserDetails(user);
}
}
Что здесь происходит:
• Spring Security передает в метод loadUserByUsername() логин пользователя;
•мы ищем пользователя в базе данных по email ;
•если пользователь найден, создаем объект CustomUserDetails ;
•если пользователь не найден, выбрасываем UsernameNotFoundException .
Важно: UserDetailsService не проверяет пароль самостоятельно. Его задача — только загрузить пользователя по логину. Сравнение пароля выполняет DaoAuthenticationProvider
через PasswordEncoder . Именно поэтому UserDetailsService считается read-only DAO-
слоем для механизма аутентификации. (docs.spring.io)
3.4. Почему это удобнее, чем возвращать встроенный User
Можно было бы вернуть объект org.springframework.security.core.userdetails.User прямо из CustomUserDetailsService , но отдельный класс CustomUserDetails дает несколько преимуществ:
•проще расширять логику в будущем;
•можно сохранить доступ к исходной сущности User ;
•код становится ближе к реальным проектам, где часто нужны дополнительные поля пользователя;
•лучше видно границу между сущностью базы данных и адаптером безопасности.
Часть 4. Шифрование паролей и конфигурация безопасности
Пароли нельзя хранить в открытом виде. При регистрации пароль должен шифроваться, а при логине — сравниваться через тот же механизм шифрования.
8
4.1. Создайте конфигурационный класс SecurityConfig
package org.example.config;
import lombok.RequiredArgsConstructor;
import org.example.security.CustomUserDetailsService; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import
org.springframework.security.config.annotation.authentication.configuration.AuthenticationConf import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import
org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableMethodSecurity
@RequiredArgsConstructor public class SecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
@Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(customUserDetailsService); provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws
9
