← Назад к вопросам
Опиши путь запроса от отправки до получения токена
2.0 Middle🔥 151 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Путь запроса: от отправки до получения JWT токена
Это классический вопрос об аутентификации. Расскажу подробно о полном цикле обработки запроса на основе моего опыта с Spring Security и OAuth2/JWT.
1. Клиент отправляет учётные данные
// Клиент (FrontEnd или мобильное приложение)
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123"
}
2. Запрос попадает в Filter Chain
Spring Security имеет цепочку фильтров, которая обрабатывает каждый запрос:
// Цепочка фильтров срабатывает ДО попадания в Controller
SecurityFilterChain → RequestMappingHandlerMapping → Controller
↓
Filters:
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- LogoutFilter
- UsernamePasswordAuthenticationFilter // ← Точка входа для логина
- CorsFilter
- CsrfFilter
- ExceptionTranslationFilter
- AuthorizationFilter
3. Обработка в контроллере аутентификации
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
private final AuthenticationManager authManager;
private final JwtTokenProvider jwtProvider;
private final UserService userService;
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
try {
// Шаг 1: Валидация входных данных
validateLoginRequest(request);
// Шаг 2: Создание объекта Authentication для обработки
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(
request.getEmail(),
request.getPassword()
);
// Шаг 3: Передача в AuthenticationManager
Authentication authenticatedToken = authManager.authenticate(token);
// Шаг 4: Получение информации пользователя
UserDetails userDetails = (UserDetails) authenticatedToken.getPrincipal();
// Шаг 5: Генерация JWT токена
String jwtToken = jwtProvider.generateToken(userDetails);
// Шаг 6: Возврат ответа клиенту
return ResponseEntity.ok(new LoginResponse(jwtToken, "Bearer"));
} catch (BadCredentialsException e) {
throw new UnauthorizedException("Invalid email or password");
}
}
}
4. Процесс аутентификации (AuthenticationManager)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
return new ProviderManager(
new DaoAuthenticationProvider() {{
setUserDetailsService(userDetailsService);
setPasswordEncoder(passwordEncoder);
}}
);
}
}
// UserDetailsService реализация
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getEmail())
.password(user.getPasswordHash()) // уже захеширован
.authorities(user.getRoles()) // ROLE_USER, ROLE_ADMIN
.accountNonExpired(true)
.accountNonLocked(!user.isLocked())
.credentialsNonExpired(true)
.enabled(user.isActive())
.build();
}
}
5. Генерация JWT токена
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration:86400000}") // 24 часа в миллисекундах
private long jwtExpirationInMs;
public String generateToken(UserDetails userDetails) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(userDetails.getUsername()) // email
.claim("roles", userDetails.getAuthorities())
.claim("userId", getUserId(userDetails)) // Дополнительные данные
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
}
6. Структура JWT токена
JWT Token = Header.Payload.Signature
Header: {"alg": "HS512", "typ": "JWT"}
Payload: {
"sub": "user@example.com",
"roles": ["ROLE_USER"],
"userId": "123e4567-e89b-12d3-a456-426614174000",
"iat": 1704067200,
"exp": 1704153600
}
Signature: HMACSHA512(base64(header) + "." + base64(payload), secret)
7. Возврат ответа клиенту
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: jwt=eyJhbGc...; Path=/; HttpOnly; Secure
{
"token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1wbGUuY29tIiwicm9sZXMiOlsiUk9MRV9VU0VSIl0sImlhdCI6MTcwNDA2NzIwMCwiZXhwIjoxNzA0MTUzNjAwfQ.qNL_...",
"tokenType": "Bearer",
"expiresIn": 86400,
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "user@example.com",
"roles": ["ROLE_USER"]
}
}
8. Использование токена в последующих запросах
// Клиент отправляет токен в заголовке
GET /api/v1/users/profile
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...
// Spring Security автоматически валидирует токен
@GetMapping("/profile")
@PreAuthorize("hasRole(USER)")
public ResponseEntity<UserProfileDTO> getProfile(
@AuthenticationPrincipal UserDetails userDetails) {
return ResponseEntity.ok(userService.getUserProfile(userDetails.getUsername()));
}
9. Жизненный цикл запроса: полная диаграмма
Клиент
↓
HTTP POST /api/v1/auth/login
↓
ServletDispatcher
↓
SecurityFilterChain
├─ SecurityContextPersistenceFilter
├─ AuthorizationFilter
└─ ExceptionTranslationFilter
↓
AuthController.login()
├─ validateLoginRequest()
├─ authManager.authenticate(UsernamePasswordAuthenticationToken)
│ ├─ DaoAuthenticationProvider
│ ├─ loadUserByUsername()
│ ├─ passwordEncoder.matches()
│ └─ return Authentication with UserDetails
├─ jwtProvider.generateToken(UserDetails)
└─ return LoginResponse with JWT
↓
Response (200 OK с токеном)
↓
Клиент получает токен и сохраняет его
↓
В следующих запросах отправляет в Authorization: Bearer <token>
10. Ключевые моменты для собеседования
Безопасность:
- Пароль никогда не передаётся открытым текстом (HTTPS)
- Пароли хешируются (BCrypt, Argon2)
- JWT подписан и не может быть подделан без секрета
- HTTPS + Secure флаг на cookie = защита от MITM
Производительность:
- Валидация токена не требует обращения в БД (всё в JWT)
- Токен хранится на клиенте, не нужна сессия на сервере
- Масштабируемость: любой сервер может валидировать токен
Отказ от доступа:
- Токен с истёкшей датой (exp) будет отклонен
- Blacklist токенов для logout
- Refresh tokens для продления сессии