← Назад к вопросам
Как просиходит алгоритм аутентификации и авторизации через JWT token в Spring Security
2.0 Middle🔥 171 комментариев
#REST API и микросервисы#Spring Boot и Spring Data#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Аутентификация и авторизация через JWT в Spring Security
Краткий ответ
Spring Security с JWT работает по следующему алгоритму:
- Аутентификация: пользователь отправляет учетные данные, сервер проверяет их и выдает JWT токен
- Хранение токена: клиент сохраняет токен (обычно в localStorage/sessionStorage)
- Авторизация: при каждом запросе клиент отправляет токен в заголовке Authorization
- Валидация токена: Spring Security проверяет подпись токена и извлекает данные пользователя
- Выдача доступа: если токен валиден, запрос обрабатывается с правами пользователя
Что такое JWT (JSON Web Token)?
JWT состоит из трех частей, разделенных точками:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. Header (заголовок)
{
"alg": "HS256",
"typ": "JWT"
}
Содержит информацию об алгоритме шифрования и типе токена.
2. Payload (полезная нагрузка)
{
"sub": "user123",
"email": "user@example.com",
"roles": ["ADMIN", "USER"],
"iat": 1516239022,
"exp": 1516242622
}
Содержит данные о пользователе и стандартные claims:
- sub (subject): ID пользователя
- email: электронная почта
- roles: роли пользователя
- iat (issued at): время выдачи
- exp (expiration): время истечения
3. Signature (подпись)
HMACSHA256(base64(header) + "." + base64(payload), secret)
Подпись подтверждает, что токен не был изменен.
Полный процесс в Spring Security
Этап 1: Аутентификация (вход в систему)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
// 1. Создание объекта аутентификации
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
// 2. Spring Security проверяет учетные данные
// (вызывает UserDetailsService.loadUserByUsername())
// 3. Установка контекста безопасности
SecurityContextHolder.getContext().setAuthentication(authentication);
// 4. Генерация JWT токена
String jwt = jwtTokenProvider.generateToken(authentication);
// 5. Возврат токена клиенту
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
}
Этап 2: Генерация JWT токена
@Component
public class JwtTokenProvider {
@Value("${app.jwtSecret}")
private String jwtSecret;
@Value("${app.jwtExpirationInMs}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
// 1. Создание payload
return Jwts.builder()
.setSubject(String.valueOf(userPrincipal.getId()))
.claim("email", userPrincipal.getEmail())
.claim("roles", userPrincipal.getAuthorities())
.setIssuedAt(now)
.setExpiration(expiryDate)
// 2. Подпись токена с использованием secret key
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
}
Этап 3: Хранение токена на клиенте
// JavaScript / React
const response = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ username: 'user', password: 'pass' })
});
const data = await response.json();
const token = data.token;
// Сохранение в localStorage
localStorage.setItem('jwtToken', token);
// Отправка в следующих запросах
fetch('/api/users/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
});
Этап 4: Валидация токена (на сервере)
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
// 1. Извлечение токена из заголовка
String jwt = getJwtFromRequest(request);
if (jwt != null && tokenProvider.validateToken(jwt)) {
// 2. Получение ID пользователя из токена
Long userId = tokenProvider.getUserIdFromToken(jwt);
// 3. Загрузка пользователя из БД
UserDetails userDetails = userDetailsService.loadUserById(userId);
// 4. Создание объекта Authentication
Authentication authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
// 5. Установка контекста безопасности
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7); // "Bearer " = 7 символов
}
return null;
}
}
Валидация токена
public boolean validateToken(String token) {
try {
// 1. Проверка подписи
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token: {}", ex);
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token: {}", ex);
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token: {}", ex);
} catch (SignatureException ex) {
logger.error("Invalid JWT signature: {}", ex);
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty: {}", ex);
}
return false;
}
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
Этап 5: Регистрация фильтра в конфигурации Security
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
// Добавление фильтра перед стандартным UsernamePasswordAuthenticationFilter
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Полный цикл запроса
1. Клиент → POST /api/auth/login с username и password
2. Server (AuthController):
- Проверка учетных данных через AuthenticationManager
- Если верно → генерация JWT
- Возврат JWT клиенту
3. Клиент сохраняет JWT в localStorage
4. Клиент → GET /api/users/profile с заголовком Authorization: Bearer <JWT>
5. Server (JwtAuthenticationFilter):
- Извлечение токена из заголовка
- Валидация подписи (проверка secret)
- Проверка срока действия
- Извлечение данных пользователя из payload
- Создание SecurityContext с правами пользователя
6. Server (бизнес-логика):
- Обработка запроса с полными правами пользователя
- Возврат данных
7. Клиент получает ответ с данными
Преимущества JWT
- Stateless: серверу не нужно хранить сессии
- Масштабируемость: работает с несколькими серверами без синхронизации
- Мобильные приложения: удобно передается в заголовках
- Стандарт: широко поддерживается
- CORS-friendly: можно использовать с фронтом на другом домене
Недостатки JWT
- Размер: больше, чем обычная сессия
- Невозможность отзыва: токен действует до истечения (нужен черный список)
- Безопасность: secret должен быть тайным и 32+ символа
Пример конфигурации в application.yml
app:
jwtSecret: "your-256-bit-secret-key-must-be-very-secret-and-long"
jwtExpirationInMs: 3600000 # 1 час
Вывод
JWT в Spring Security — это мощный механизм для создания масштабируемых API. Алгоритм основан на выдаче криптографически подписанного токена, который клиент отправляет с каждым запросом. Сервер валидирует подпись без необходимости доступа к БД для каждого запроса, что делает систему очень производительной.