Как добавить кастомную логику аутентификации или авторизации в Spring Security?

«Как добавить кастомную логику аутентификации или авторизации в Spring Security?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Кастомизация в Spring Security обычно реализуется через расширение ключевых компонентов.

1. Кастомная аутентификация (например, по JWT):

  • Создайте фильтр для извлечения и валидации токена.
  • Реализуйте AuthenticationProvider или используйте стандартный, установив кастомный UserDetailsService.
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) {
        // Ваша логика загрузки пользователя из БД или другого источника
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        // Преобразование ролей/прав в GrantedAuthority
        List<GrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
                .collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                authorities
        );
    }
}

2. Кастомная конфигурация доступа: Настройте SecurityFilterChain, чтобы определить правила для разных эндпоинтов и подключить свои фильтры.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(authz -> authz
            .requestMatchers("/api/admin/**").hasRole("ADMIN")
            .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
            .requestMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
        )
        // Добавление кастомного JWT-фильтра перед стандартным
        .addFilterBefore(new JwtAuthenticationFilter(), 
                         UsernamePasswordAuthenticationFilter.class)
        .csrf(csrf -> csrf.disable()) // Отключаем для stateless API
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    return http.build();
}

3. Кастомная логика авторизации: Для сложных правил (например, на основе атрибутов объекта) реализуйте @PreAuthorize с SpEL или создайте собственный PermissionEvaluator.