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

Как Spring Security сопоставляет обращение на сервер с клиентом

2.0 Middle🔥 201 комментариев
#Spring Framework#Безопасность

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

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

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

# Как Spring Security сопоставляет обращение на сервер с клиентом

Это важный аспект безопасности веб-приложений. Spring Security использует несколько механизмов для аутентификации и отслеживания клиента через несколько запросов.

Основная задача

HTTP — это stateless протокол. Каждый запрос независим от предыдущих. Но нам нужно знать, кто это (аутентификация) и оставаться в системе между запросами (session).

Механизм 1: Session + Cookies

Это самый распространённый способ:

Первый запрос (логин):
1. Клиент отправляет username и password
2. Сервер аутентифицирует
3. Сервер создаёт Session в памяти (обычно в SessionRepository)
4. Сервер возвращает JSESSIONID cookie клиенту

Последующие запросы:
1. Браузер автоматически отправляет JSESSIONID cookie
2. Сервер находит Session по ID
3. Сервер извлекает аутентифицированного пользователя из Session

В Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
                .sessionFixationProtection(SessionFixationProtectionStrategy.MIGRATEDSESSION)
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        return http.build();
    }
}

Механизм 2: Authentication Object

В контексте текущего запроса Spring Security хранит информацию о пользователе в объекте Authentication:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
    String username = auth.getName();
    Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
}

Механизм 3: SecurityContext + ThreadLocal

Spring Security использует ThreadLocal для хранения контекста безопасности в рамках одного запроса:

Поток 1 (запрос 1)
├── SecurityContext = User A, Role ADMIN
├── UserController.getCurrentUser() -> User A
└── После завершения запроса -> очистка ThreadLocal

Поток 2 (запрос 2, другой клиент)
├── SecurityContext = User B, Role USER
├── UserController.getCurrentUser() -> User B
└── После завершения запроса -> очистка ThreadLocal
// В контроллере
@RestController
public class UserController {
    @GetMapping("/me")
    public User getCurrentUser() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        return (User) auth.getPrincipal();
    }
}

Механизм 4: JWT (JSON Web Token) — для API

Для stateless API используют JWT:

Первый запрос (логин):
1. Клиент отправляет credentials
2. Сервер проверяет и создаёт JWT (содержит: user id, roles, expiry)
3. Сервер возвращает JWT клиенту

Последующие запросы:
1. Клиент отправляет JWT в Authorization header
2. Сервер парсит и валидирует JWT (подпись, expiry)
3. Сервер создаёт Authentication object из JWT
@Configuration
@EnableWebSecurity
public class JwtSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)  // Никаких сессий!
            .and()
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
    
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
        String token = extractJwtFromRequest(req);
        if (token != null && validateJwt(token)) {
            String userId = getUserIdFromJwt(token);
            // Создаём Authentication для этого запроса
            Authentication auth = new UsernamePasswordAuthenticationToken(
                userId, null, getAuthoritiesFromJwt(token)
            );
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(req, res);
    }
}

Механизм 5: Distributed Sessions

Для масштабируемых систем с несколькими серверами используют централизованное хранилище сессий:

Клиент -> Балансировщик нагрузки
           ├-> Сервер 1
           ├-> Сервер 2
           └-> Сервер 3
           
Все серверы смотрят в одно место (Redis, MongoDB) за Session
@Configuration
@EnableSpringHttpSession
public class HttpSessionConfig {
    @Bean
    public LettuceConnectionFactory connectionFactory() {
        return new LettuceConnectionFactory();
    }
}

Фильтры Spring Security

Spring Security использует цепочку фильтров для обработки каждого запроса:

FilterChain:
1. ExceptionTranslationFilter
2. FilterSecurityInterceptor
3. UsernamePasswordAuthenticationFilter (обработка логина)
4. JwtAuthenticationFilter (валидация JWT)
5. AnonymousAuthenticationFilter (анонимный пользователь)
...

Каждый фильтр проверяет запрос и либо пропускает его дальше, либо блокирует.

Практический пример: Session-based

@RestController
public class AuthController {
    private UserRepository userRepository;
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest req, HttpSession session) {
        User user = userRepository.findByUsername(req.getUsername());
        if (user != null && passwordEncoder.matches(req.getPassword(), user.getPassword())) {
            // Spring Security автоматически создаст сессию
            Authentication auth = new UsernamePasswordAuthenticationToken(
                user, null, user.getAuthorities()
            );
            SecurityContextHolder.getContext().setAuthentication(auth);
            return ResponseEntity.ok("Logged in");
        }
        return ResponseEntity.status(401).body("Invalid credentials");
    }
    
    @GetMapping("/protected")
    public String protectedResource() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        User user = (User) auth.getPrincipal();
        return "Hello, " + user.getUsername();
    }
}

Вывод

Spring Security сопоставляет запросы с клиентами через:

  1. Session + Cookies — для традиционных веб-приложений
  2. SecurityContext + ThreadLocal — для текущего запроса
  3. JWT — для stateless API
  4. Distributed Sessions — для масштабируемых систем
  5. Фильтры — для перехвата и валидации каждого запроса

Выбор метода зависит от архитектуры приложения и требований к масштабируемости.