Spring Security — это: ✓ Какая-то приблуда для безопасности ✓ Очередной фреймворк зоопарка Spring ✓ “Коробочное” решение для защиты приложений от рисков ИБ 5 Интуитивное представление: ОК ✔
Аутентификация Отвечает на вопрос: “Ты ли это?” Имеет дело с логинами и паролями. Устраняем любимую путаницу Авторизация Отвечает на вопрос: “Можно ли тебе сюда?” Имеет дело с правами, ролями, доступами... 7 Наша главная тема сегодня Но начнём с этой
Интерфейс AccessDecisionManager 11 ✢ Центральный компонент авторизации ✢ Опрашивает других участников выбора "пускать или нет" ✢ Обычно делегирует имплементациям интерфейса AccessDecisionVoter
Авторизация в веб-приложениях 13 ✢ Включается через @EnableWebSecurity ✢ Работает как 1 фильтр FilterChainProxy ✢ Делегирует внутренним фильтрам (бинам)
Фильтры собираются в цепочки 15 ✢ Цепочек может быть несколько ✢ На каждый запрос срабатывает только одна ✢ В spring-boot-starter-security из коробки 6 цепочек: ○ Для статики ○ Для ошибок, для ... ○ Для всего остального 11 фильтров
Авторизация на уровне методов 18 ✢ Включается через аннотацию: @EnableGlobalMethodSecurity(securedEnabled=true) ✢ Работает как прокси (можно на AOP) ✢ При нарушении доступа выбрасывает: AccessDeniedException
Варианты авторизации для методов 19 @Service public class MyService { @Secured("ROLE_USER") public String secure() { return "Hello Security"; } } @PreAuthorize("hasRole('ADMIN')") @PostAuthorize("hasPermission(filterObject, 'read')") SpEL Но должно же у них быть что-то общее?
Что содержит Authentication 24 principal — это обычно пользователь (наследник UserDetails; у нас это карта) credentials — это обычно пароль (String) authorities — список разрешений/доступов details — что угодно (IP, свойства и т.п.) authenticated — признак успешной проверки
SecurityContext доступен отовсюду 25 ✢ Напрямую через SecurityContextHolder: SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); assert authentication.isAuthenticated(); ✢ Опосредованно через аннотации: @RequestMapping("/foo") public String foo(@AuthenticationPrincipal User user) { // do stuff with user } почти
SecurityContext доступен отовсюду 26 ✢ Опосредованно из сервлет-контейнера: @RequestMapping("/foo") public String foo(Principal principal) { Authentication authentication = (Authentication) principal; User user = (User) authentication.getPrincipal(); // do stuff with user } почти Но откуда он попадает во все эти места?
“Spring Security is fundamentally thread bound because it needs to make the current authenticated principal available to a wide variety of downstream consumers” 27 По словам авторов
“Fundamentally thread bound” 28 ✢ В каждом потоке свой SecurityContext ✢ Как правило, за нас это делает Spring Security ✢ Но иногда можно/нужно самим: @Configuration public class ApplicationConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { return new DelegatingSecurityContextExecutorService(newFixedThreadPool(5)); } } А как же реактивщина?
Reactive Spring Security ⚛ 29 ✢ ReactiveSecurityContextHolder ✢ Под капотом использует reactor.util.context.Context ✢ Для веб-приложений работает за счёт ReactorContextWebFilter ✢ Нуждается в @EnableWebFluxSecurity и @EnableReactiveMethodSecurity
Интерфейс AuthenticationManager 32 public interface AuthenticationManager { Authentication authenticate(Authentication authentication) throws AuthenticationException; } Является входной точкой во всю логику аутентификации Spring Security
Варианты ответа метода authenticate() 33 ✢ Объект Authentication (authenticated=true) ⇒ Всё ОК ✅ ✢ Исключение AuthenticationException ⇒ Пшёлвон ⛔ ✢ null, просто null ⇒ Я не в курсе, спроси другого ♂
Класс ProviderManager 34 ✢ Основная имплементация для AuthenticationManager’а ✢ Делегирует набору AuthenticationProvider’ов ✢ Швыряет ProviderNotFoundException, если не находит подходящего провайдера
Как создать AuthenticationManager’а 38 Способы: ✢ С помощью AuthenticationManagerBuilder ♂ ✢ Вручную, с нуля Области: ✢ Глобально на всё приложение ✢ Локально на заданную часть
Пример 2: глобально, с нуля 40 @Bean public AuthenticationManager globalAuthenticationManager( PasswordAuthenticationProvider passwordAuthenticationProvider, PinAuthenticationProvider pinAuthenticationProvider) { var providerManager = new ProviderManager(List.of( passwordAuthenticationProvider, pinAuthenticationProvider)); // всякие манипуляции с этим объектом return providerManager; } Перекроет бин, поставляемый spring-boot-starter-security
Интерфейс UserDetailsService 43 public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; } Является "мостом" между прикладным хранилищем учётных данных и Spring Security
Превратности контракта UserDetails 47 ✢ Метод getUserName() никогда не должен возвращать null. ✢ Метод getPassword() может вернуть null: ○ Критичные данные могут стираться из памяти после успешной аутентификации. ○ См. CredentialsContainer#eraseCredentials Кстати, о паролях...
Класс DelegatingPasswordEncoder 53 ✢ Используется в Spring Security по умолчанию ✢ Умеет “совершенствовать” хэши паролей ✢ Удобно создавать через PasswordEncoderFactories
Алгоритмы хэширования паролей 54 SHA-1, SSHA (ldap), SHA-256, MD-5, MD-4 Не безопасны, не рекомендованы, вызывают головную боль и предупреждения в IDE ➖ scrypt, bcrypt Широко распространённые варианты (не только в экосистеме JVM) pbkdf2, argon2 Современные намеренно-ресурсоёмкие алгоритмы
Алгоритм Argon2 в Spring Security 55 ✢ Реализован в org.bouncycastle:bcprov-jdk15on:1.64 ✢ Используется через класс Argon2PasswordEncoder ✢ Генерирует соль из SecureRandom ⇒ один пароль всякий раз даёт разные хэши
Кратко о Spring Security в целом 58 ✢ Помогает с аутентификацией и авторизацией ✢ Обеспечивает гибкость через интерфейсы ✢ Состоит из модулей (core, web, config, ldap, ...)
Резюме по авторизации 59 ✢ Работает в веб-приложениях за счёт фильтров ✢ Умеет защищать методы через прокси-обёртки ✢ Основные классы: ○ @EnableWebSecurity, @EnableGlobalMethodSecurity ○ AccessDecisionManager, AccessDecisionVoter ○ @PreAuthorize, @PostAuthorize
Резюме по аутентификации 60 ✢ Является “скелетом” для подключения разных механизмов проверки ✢ Предоставляет средства работы с паролями ✢ Основные классы: ○ Authentication, AuthenticationManager ○ SecurityContext, SecurityContextHolder ○ UserDetails, UserDetailsService
Credits Special thanks to all the people who made and released these awesome resources for free: ✢ Presentation template by SlidesCarnival ✢ Photographs by Unsplash 66