← Назад к вопросам

Как Spring Security выбирает кому дать доступ к данным

2.0 Middle🔥 231 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как Spring Security выбирает кому дать доступ к данным

Spring Security реализует многоуровневую систему контроля доступа, которая работает через сочетание аутентификации (проверка личности) и авторизации (проверка прав доступа).

Основные компоненты

1. AuthenticationManager

Первый уровень — аутентификация. AuthenticationManager проверяет учётные данные пользователя (логин/пароль, токен и т.д.) и создаёт объект Authentication.

@Bean
public AuthenticationManager authenticationManager(
        UserDetailsService userDetailsService,
        PasswordEncoder passwordEncoder) {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(userDetailsService);
    provider.setPasswordEncoder(passwordEncoder);
    return new ProviderManager(provider);
}

2. Authentication объект

После успешной аутентификации создаётся объект Authentication, содержащий:

  • Principal (кто это — обычно UserDetails)
  • Credentials (пароль, но обычно удаляется после аутентификации)
  • Authorities (роли и права: ROLE_USER, ROLE_ADMIN и т.д.)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName(); // получить имя
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();

3. SecurityContext

Authentication объект хранится в SecurityContext, который доступен во всей сессии:

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
boolean isAuthenticated = authentication.isAuthenticated();

Авторизация — управление доступом

AccessDecisionManager

Это главный компонент, который решает, дать ли доступ к ресурсу. Есть три стратегии:

1. AffirmativeBased (по умолчанию) — доступ дан, если хотя бы один Voter проголосовал за:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasRole("USER")
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin();
        return http.build();
    }
}

2. UnanimousBased — доступ дан только если ВСЕ Voters проголосовали за:

http
    .authorizeHttpRequests(auth -> auth
        // Все проверки должны пройти
        .requestMatchers("/critical/**").hasRole("ADMIN")
        .requestMatchers("/critical/**").hasRole("VERIFIED")
    );

3. ConsensusBased — доступ дан, если больше голосов за, чем против:

@Bean
public AccessDecisionManager consensusAccessDecisionManager() {
    List<AccessDecisionVoter<?>> voters = new ArrayList<>();
    voters.add(new RoleVoter());
    voters.add(new WebExpressionVoter());
    return new ConsensusBased(voters);
}

FilterSecurityInterceptor — точка принятия решения

У каждого URL определены требования доступа. Фильтр проверяет Authentication перед тем, как пропустить запрос:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasAnyRole("ADMIN", "SUPER_ADMIN")
                .requestMatchers("/profile/**").authenticated()
                .requestMatchers("/", "/login", "/register").permitAll()
                .anyRequest().denyAll()
            );
        return http.build();
    }
}

Типы проверок доступа

1. По ролям

.hasRole("ADMIN")  // требует ROLE_ADMIN
.hasAnyRole("ADMIN", "MODERATOR")

2. По授权

.hasAuthority("DELETE_USER") // точное имя permission
.hasAnyAuthority("READ", "WRITE")

3. SpEL выражения

.access("hasRole('ADMIN') and #user.id == principal.id")

4. Кастомные проверки

@PostAuthorize("returnObject.owner.id == authentication.principal.id")
public Post getPost(Long id) {
    return postRepository.findById(id);
}

Полный процесс проверки доступа

  1. Запрос приходит → FilterSecurityInterceptor перехватывает
  2. Получение Authentication → из SecurityContext
  3. Получение требований → какие роли нужны для этого URL
  4. Вызов AccessDecisionManager → сравнение Authentication с требованиями
  5. Voting → каждый Voter голосует (за/против/воздержался)
  6. Результат → доступ дан или AccessDeniedException

Пример с методами

@Service
public class UserService {
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteUser(Long userId) {
        // только ADMIN может удалить
    }
    
    @PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
    public User getUser(Long userId) {
        // можешь получить свой профиль или ты админ
    }
    
    @PreAuthorize("hasAnyRole('ADMIN', 'MODERATOR')")
    public void banUser(Long userId) {
        // админ или модератор
    }
}

Резюме

Spring Security даёт доступ на основе:

  1. Аутентификации (кто ты — проверка credentials)
  2. Authorities/Roles (какие у тебя права)
  3. ConfigAttributes (что требуется для ресурса)
  4. AccessDecisionManager (сравнение и голосование)

Этот многоуровневый подход позволяет реализовать гибкую и безопасную систему управления доступом.

Как Spring Security выбирает кому дать доступ к данным | PrepBro