Spring Security – мощный фреймворк для обеспечения безопасности Java-приложений в экосистеме Spring. Он предоставляет комплексные сервисы безопасности (аутентификация, авторизация, фильтрация запросов и т.д.) для корпоративных веб-приложений. Spring Security возник из-за того, что встроенные в Java EE механизмы безопасности (Servlet/EJB) оказались слишком ограниченными и мало портируемыми для реальных задач. С помощью Spring Security можно гибко настроить проверку личности пользователя, разграничение прав доступа, защиту от CSRF, встроенную поддержку «remember-me», сессию, а также десятки других возможностей. Фреймворк автоматически интегрируется со Spring MVC/Boot и даже сам генерирует простые страницы логина/выхода по умолчанию
Основные понятия:
Аутентификация (Authentication). Процесс проверки и установления личности пользователя (principal) приложения. Обычно происходит проверка логина и пароля. Spring Security поддерживает множество моделей аутентификации (формовая, HTTP Basic/Digest, LDAP, JWT, OAuth2 и др.), при этом наиболее часто после успешного входа формируется объект Authentication
, содержащий информацию о пользователе
Авторизация (Authorization). Процесс принятия решения, разрешено ли аутентифицированному пользователю выполнить запрошенное действие или получить ресурс. После аутентификации Spring Security сравнивает роли/привилегии пользователя с требованиями защищаемого ресурса. Например, проверяется, имеет ли пользователь роль ADMIN
или другую необходимую полномочие.
Фильтры безопасности (Security Filters). Весь механизм Spring Security построен как цепочка Servlet-фильтров. Эти фильтры перехватывают каждый HTTP-запрос до попадания в контроллер, проверяя аутентификацию и авторизацию. Spring Security по умолчанию подключается в FilterChain через DelegatingFilterProxy
. Фильтры могут, например, перенаправлять на страницу логина, если пользователь не аутентифицирован, или блокировать доступ, если прав недостаточно
Контекст безопасности (Security Context). Информация о текущем пользователе (принципале) хранится в объекте SecurityContext
, привязанном к текущему потоку исполнения. Обычно Spring Security использует SecurityContextHolder
с ThreadLocal
для хранения текущего Authentication
(информация о пользователе и его правах). Благодаря этому из любого места кода (в рамках одного HTTP-запроса) можно получить текущий контекст безопасности и узнать, кто залогинен. Spring Security самостоятельно очищает этот контекст после завершения обработки запроса
Роли и привилегии (Roles and Authorities). В Spring Security права доступа пользователя задаются через «предоставленные полномочия» (GrantedAuthority
). Роли – это лишь особый тип полномочий. По соглашению роли записываются с префиксом ROLE_
(например, роль администратора называется ROLE_ADMIN
). Если в конфигурации указывается требование hasRole("ADMIN")
, то Spring Security автоматически проверит, есть ли у пользователя полномочие ROLE_ADMIN
. Таким образом «роль» – это только строковый маркер («ROLE_X»), а реальное решение принимается сравнением значений GrantedAuthority
у текущего Authentication
с требуемыми атрибутами
Spring Security работает с помощью фильтров. Фильтры последовательно обрабатывают пришедший HTTP-запрос и решают, обрабатывать его дальше или нет. Фильтры образуют цепочку обязанностей:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
@Bean
@SneakyThrows
public SecurityFilterChain securityFilterChain(HttpSecurity http) {
return http
.csrf().disable()
.cors().disable()
.authorizeHttpRequests(customizer -> customizer.anyRequest().authenticated())
.httpBasic()
.authenticationEntryPoint((request, response, authException) -> response.sendError(401))
.and()
.build();
}
}
В этом примере мы:
Access-Control-Allow-Origin: https://example.com
. Тогда браузер обработает егоПо умолчанию, фильтры такие:
WebAsyncManagerIntegrationFilter
-Интеграция безопасности с асинхронными запросами (например, @Async
)SecurityContextPersistenceFilter
- Загружает/сохраняет SecurityContext
в HttpSession
(или другую стратегию) устанавливает его в SecurityContextHolder
для текущего потока. Если контекст ещё не создан (первый запрос), он создаёт новый «пустой» контекст. Это обеспечивает получение информации о ранее вошедшем пользователеHeaderWriterFilter
- Добавляет HTTP-заголовки безопасности (например, X-Frame-Options
)CsrfFilter
- Обрабатывает защиту от CSRF-атак (если включена)LogoutFilter
- Обрабатывает POST-запросы на /logout
. В этом случае удаляется CSRF-токен, завершается сессия, чистится SecurityContextHolder
BasicAuthenticationFilter
- Обрабатывает HTTP Basic авторизацию (если используется): извлекает логин\пароль и передает их в AuthenticationManager
AuthenticationManager
представляет из себя интерфейс с одним методом:
Authentication authenticate(Authentication authentication) throws AuthenticationException;
В нашем случае имплементацией Authentication
будет UsernamePasswordAuthenticationToken
AuthenticationManager
получает объект Authentication
(например, с username и password) и передаёт его в подходящий AuthenticationProvider
, чтобы проверить подлинность. AuthenticationProvider
содержит в себе два метода:
Authentication authenticate(Authentication authentication) throws AuthenticationException;
boolean supports(Class<?> authentication);
Обычно реализуется через ProviderManager
, который внутри содержит список AuthenticationProvider
‘ов.
AuthenticationProvider
проверяет, поддерживает ли он данный тип Authentication
с помощью метода supports
. Если поддерживает — проверяет credentials в методе authenticate
(например, сверяет пароль и логин), а если аутентификация успешна — возвращает полностью заполненный объект Authentication
с флагом authenticated=true
.
Если все провайдеры выкинул исключение, ProviderManager
выбросит AuthenticationManager
последнее исключение
Далее фильтр сохраняет полученный Authentication
в SecurityContextHolder
. Если выбросится AuthenticationException
от провайдеров, то будет сброшен контекст, и вызовется AuthenticationEntryPoint
RequestCacheAwareFilter
- Кэширует защищённые запросы, чтобы потом на них вернуть пользователя:
SecurityContextHolderAwareRequestFilter
- Делает request.isUserInRole(...)
и getUserPrincipal()
работающимиAnonymousAuthenticationFilter
- Назначает “анонимного пользователя”, если пользователь не аутентифицирован. Фильтр заполняет объект SecurityContextHolder
анонимной аутентификацией AnonymousAuthenticationToken
с ролью ROLE_ANONYMOUS
. Это гарантирует что в SecurityContextHolder
будет объектSessionManagementFilter
- Производится действия, связанные с сессией. Это может быть:
SecurityContext
в securityContextRepository
В обычном случае происходит следующее: SecurityContextRepository
с дефолтной реализацией HttpSessionSecurityContextRepository
сохраняет SecurityContext
в сессию. Вызывается sessionAuthenticationStrategy.onAuthentication
:
ExceptionTranslationFilter
- Перехватывает исключения (например, недостаточно прав или ошибка аутентификации) из более глубоких фильтров. Если пользователь не аутентифицирован, ExceptionTranslationFilter
перенаправит его на точку входа (например, на страницу логина) или вернёт HTTP 401/403. Этот механизм обеспечивает корректное реагирование на проблемы с безопасностью.FilterSecurityInterceptor
- Проверяет соответствие между требуемыми правами доступа и правами текущего пользователя. Здесь происходит сравнение атрибутов защищаемого ресурса (например, требуемой роли) с полномочиями в Authentication
. Если прав достаточно, запрос пропускается к контроллеру, иначе – генерируется отказ в доступе.После обработки запроса всеми фильтрами результат либо отдается клиенту (запрос был разрешён), либо возвращается ошибка безопасности. При этом при завершении запроса SecurityContextPersistenceFilter
может сохранить обновлённый SecurityContext
(например, если пользователь только что вошёл) обратно в сессию.
Также есть:
RememberMeAuthenticationFilter
, читающий куки и восстанавливающий аутентификациюOAuth2LoginAuthenticationFilter
для OAuth2-аутентификацииКонфигурация Spring Security может выполняться чисто на Java (без XML). Современный подход – объявлять бины SecurityFilterChain
и UserDetailsService
или WebSecurityCustomizer
. Начиная с Spring Security 5.7 класс WebSecurityConfigurerAdapter
считается устаревшим, и вместо него рекомендуется использовать SecurityFilterChain
. Например, класс конфигурации может выглядеть так:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
В этом примере все запросы к приложению требуют аутентификации (anyRequest().authenticated()
), и используется HTTP Basic для ввода логина/пароля. Пользователь с именем user
, паролем password
и ролью USER
хранится в памяти внутри InMemoryUserDetailsManager
. Метод withDefaultPasswordEncoder()
упрощает демонстрацию (пароль автоматически кодируется), но не предназначен для продакшен-использования – в реальном приложении следует хранить пароль в безопасном зашифрованном виде.
При подобной конфигурации Spring Boot создаст простую стандартную форму логина/логаута по умолчанию. Как только разработчик добавляет Spring Security, по умолчанию все URL блокируются и требуют входа (пока не настроено иное поведение). Поведение фильтров (какие URL защищать, какие – открыты) меняется через методы authorizeHttpRequests()
, permitAll()
, hasRole()
и т.д. Дополнительно можно настраивать CSRF-защиту, правила для статических ресурсов, перенаправления и прочее через объект HttpSecurity
.
Если не настраивать пользователей вручную, Spring Boot по умолчанию создаст одного пользователя с именем user
и сгенерированным паролем, который выводится в логи при запуске. Но обычно для учебных примеров и небольших приложений проще явно указать своих пользователей (как выше).
Продвинутые возможности:
Authentication
вручную. Spring Security полностью поддерживает работу с JWT (например, через модули spring-security-oauth2-resource-server
).AuthenticationProvider
или фильтр, чтобы проверить пользователя по 2FA, LDAP, внешним сервисам и т.д. Spring Security предоставляет точки расширения (например, можно сохранить AuthenticationManager
как бин и вставлять свои фильтры) – фреймворк изначально спроектирован как открытая и расширяемая платформа.JdbcUserDetailsManager
, который может читать пользователей и роли из таблиц (с помощью SQL), или делают свой сервис, реализующий UserDetailsService
, и подтягивают UserDetails
из JPA-Entitiy. Интерфейс UserDetails
в Spring Security служит как раз «адаптером» между вашей моделью пользователя в БД и тем, что нужно фреймворку. Таким образом вы можете хранить в БД произвольные поля (имя, email, статус и т.д.) и просто реализовать loadUserByUsername
, возвращая объект, унаследованный от UserDetails
, содержащий имя, пароль и список ролей. Spring Security позаботится о проверке пароля, установке Authentication в контекст и применении политик доступа.