Spring Security 6. Авторизация и аутентификация на основе Базы данных
Привет. Меня зовут Кирилл, я Java-разработчик. Несмотря на то, что уже год я работаю в компании, по-прежнему стараюсь находить время для собственных проектов, с помощью которых осваиваю интересующие меня технологии и подходы. Именно на таком проекте я решил разобраться, как работает авторизация и аутентификация на основе базы данных в Spring Security 6. Изменений, по сравнению с предыдущими версиями немало. Примеры из документации не отвечают на все вопросы в полной мере, а материалов на русском языке по этой теме я, как ни старался, не нашел. Информацию собирал из разных иностранных источников по кусочкам. Теперь поделюсь с вами найденным.
Думаю, что большая часть читателей знают, что такое авторизация и аутентификация, а также чем они отличаются друг от друга. Но на случай, если статью будут читать ученики, грубо обобщу, что авторизация — это отсечение тех, кто не имеет в целом допуска к ресурсу. Аутентификация — распределение возможностей (прав, допусков) для авторизованных пользователей. Аутентификация осуществляется на основе ролей зарегистрированного пользователя и других свойств, которых коснемся позже.
Основная проблема заключается в том, что с версии Spring Security 5.7.0 класс WebSecurityConfigurerAdapter признан устаревшим и в дальнейших версиях его использование невозможно. Большинство же существующих на данный момент гайдов опираются на наследование этого класса.
В своем материале я предложу вашему вниманию минимальный скелет сервиса с авторизацией и аутентификацией на основе базы данных, с нескольким эндпоинтами, допуски к которым регулируются ролями и уровнями доступа. Скелет будет по-настоящему минимальным, чтобы не перегружать читателя лишней информацией и логикой, при этом полностью познакомить с концепцией. В дальнейшем вы можете расширить этот скелет, добавив свою функциональность и приспособив под свои задачи.
Итак, для начала создадим новый проект через Spring Initializr.
Создание проекта
Здесь все стандартно. Выбираем любую современную версию Spring, сборка через Maven, ну и прописываем метаданные. Дополнительно подключаем Spring Web, Spring Data JPA, MySQL Driver (в примере я буду использовать MySQL, а вы можете использовать любую б/д на усмотрение) и Lombok (мне с ним удобнее).
Далее создадим базу данных с необходимыми нам полями. Обязательные для нашего проекта поля — name, password и role. В колонку с паролем пишем какой-то простой пароль (для более лёгкого запоминания и тестирования), закодированный в Bcrypt password encoder. У меня для всех 1234, закодированные со сложностью 5 (это надо запомнить и в дальнейшем применить, иначе авторизация не сработает).
Моя база для теста выглядит так
Открываем проект и прописываем нашу базу в Application Properties.
Затем я люблю запуститься и проверить, всё ли работает. Поскольку мы пока не написали класс SecurityConfig, зайдем по имени user, а пароль берём из логов.
Теперь напишем сущность User, с помощью которой будем раскладывать пользователей из базы в объекты, а также интерфейс UserRepository, расширяющий JpaRepository…
Entity User
UserRepo
Далее создадим класс TestController, где пропишем несколько эндпоинтов, к которым можно обратиться согласно уровню доступа пользователей:
welcome — страница будет показываться всем, даже без прохождения авторизации;
users — страница для тех, кто имеет роль USER в базе данных;
admins — для всех админов;
all — для всех, но после авторизации.
Простейший контроллер с распределением эндпоинтов по ролям
Сервисов с развернутой бизнес логикой в нашем примере не будет, поскольку, как я упоминал раньше, мы пишем простой, минимальный скелет, поэтому не вижу смысла перегружать способности понимания читателей.
Чтобы настроить работу SpringSecurity, создадим класс SecurityConfig, где опишем принципы авторизации и аутентификации для каждого эндпоинта, зададим UserDetailService, PasswordEncoder и остальные свойства.
Самый важный класс при работе со Spring Security
Пишем кастомный MyUserDetailsService, расширяющий UserDetailService и реализующий метод loadUserByUsername…
… и связанный с ним кастомный MyUserDetails, который переводит нашего «юзера» из базы данных в «юзера», понятного SpringSecurity. Здесь мы расширяем стандартный UserDetails и переопределяем все его методы.
Метод GetAuthorities написан так на случай, если у кого-то из базы будет несколько ролей
Запускаем проект и тестируем эндпоинты. В результате, на страницу «welcome» вход осуществляется без авторизации, на страницу «all» могут зайти все зарегистрированные пользователи, а доступ к страницам «users» и «admins» имеют соответственно пользователи с ролями USER и ADMIN.
Как я говорил ранее, в дальнейшем функциональность скелета можно расширить, навесить на него бизнес логику, добавить и сделать более интересные эндпоинты, усложнить базу юзеров и поменять архитектуру SecurityFilterChain. Эту основу несложно вшить и в любой существующий проект, немного доработав под свои свои условия. (Если будет интересно — покажу, как встроил его в свой проект электронной библиотеки).
Если у вас возникнут вопросы, буду рад ответить на них в комментариях. Также буду рад обратной связи от более матерых и искушенных разработчиков.
Репозиторий проекта здесь.