Разгребала на выходных опрос по непоняткам в спринге. Один из самых частых вопросов касается Spring Security.
Для многих это самый непонятный модуль. Он есть на каждом проекте, понимать его надо, но до чего же тяжело разбираться! Сама была в такой ситуации не раз, ловила огромное сопротивление, когда приходили задачи на секьюрити.
К счастью, с годами ситуация прояснилась. Секрет успеха по работе с Security лежит в понимании двух вещей:
🔶 Используемый механизм безопасности
LDAP, JWT, разные схемы oAuth и SSO. Роли, белые/черные списки, секреты, токены и тд. В чистом виде большинство механизмов несложные, на ютубе отлично объясняются за 10 минут.
🔶 Базовая архитектура Spring Security
Об этом и будет пост. Опишу простыми словами, что происходит в классической (не реактивной) архитектуре, и как решать задачки с секьюрити на практике.
Spring Security участвует в двух процессах:
1️⃣ Путь запроса до контроллера и обратно
Схема такая:
▪️ Сервер получает запрос
▪️ Проводит его через цепочку фильтров, у каждого из которых своя задача:
▫️ CsrfFilter проверяет CSRF токен
▫️ SessionManagementFilter разбирается с сессией
И так далее, обычно 10-15 фильтров в итоге
▪️ Запрос попадает в контроллер
▪️ Выполняется бизнес-логика проекта
▪️ Контроллер возвращает ответ
▪️ Ответ проходит по той же цепочке фильтров, но в обратном порядке. Фильтры выставят у ответа нужные заголовки, обнулят/сохранят сессию и тд
▪️ Ответ возвращается пользователю
Что тут важно:
▫️ Проверка прав, заголовков и многие секьюрити штуки часто обрабатываются в фильтрах ДО вызова контроллера
▫️ Можно поменять содержимое, заголовки и другие части ответа ПОСЛЕ выхода из контроллера
▫️ Каждый фильтр может менять запрос/ответ или контекст, поэтому порядок фильтров важен
▫️За конфигурацию фильтров отвечает метод конфига
WebSecurityConfigurerAdapter#configure(HttpSecurity http)Чтобы увидеть процесс наглядно, поставьте брейкпойнт в методе
FilterChainProxy#doFilterInternal. Там увидите все фильтры, можно погулять по ним и отследить, что происходит с запросом2️⃣ Авторизация/аутентификация
скрывается за фасадом — бином AuthenticationManager.
Чтобы авторизовать пользователя, в коде проекта нужно вызвать метод
authenticationManager.authenticateМожет в фильтре, может в контроллере, зависит от механизма безопасности.
Метод authenticate пройдётся по всем заданным источникам авторизации. Результат можно положить в SecurityContextHolder, тогда он будет доступен из любого места в коде. Также бросится событие AuthenticationSuccessEvent, но обычно его игнорируют:)
Источники авторизации называются *Provider (например, DaoAuthenticationProvider) и определяются в конфиге. В итоге всё, что нужно от разработчика это:
☝️определить провайдеры
✌️ вызвать authenticate в нужном месте
Посмотреть список текущих провайдеров: брейкпойнт в методе
ProviderManager#authenticate.Задать список провайдеров: метод
configure(AuthenticationManagerBuilder auth) в классе WebSecurityConfigurerAdapter🦄Что с этим знанием делать на практике
Если вам дали задачу, связанную с Spring Security, не бросайтесь сразу гуглить
spring security jwt/oauth/ldap example
Шанс запутаться и сделать не то стремится к 100%. Если у вас крупный и сложный проект, идеально подходящего туториала точно не будет.
Лучше проработать каждую часть по порядку:
🛡️ Разобраться в механизме безопасности, который нужно реализовать. Что, откуда и как передаётся, где, когда и как валидируется и сохраняется.
Это самый важный шаг! Обязательно подключите тестировщика, ему тоже надо в это погрузиться.
🛡️ Прикинуть, как полученная схема ложится на архитектуру секьюрити. Что сделать в фильтрах до обработки запроса, а что после. Возможно, что-то надо сделать внутри слоя сервисов
🛡️ Желательно обсудить полученное решение со старшим товарищем
Только после этого смотрите документацию, туториалы и собирайте из них решение.
Действуя по плану выше, вы потратите меньше времени и сохраните высокую самооценку🙂