Ответ
JWT (JSON Web Token) — стандарт для создания токенов доступа, широко используемый в REST API и микросервисных архитектурах.
Архитектура JWT-авторизации:
graph LR
A[Клиент] -->|1. Логин/пароль| B[Сервер авторизации]
B -->|2. Проверка учетных данных| C[(База данных)]
B -->|3. Генерация JWT| D[Клиент]
D -->|4. Запрос с токеном| E[Защищенный ресурс]
E -->|5. Верификация токена| F[Доступ к ресурсу]
1. Генерация JWT токена (на стороне сервера):
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
// Секретный ключ (должен храниться безопасно!)
String secretKey = "mySuperSecretKeyAtLeast32CharactersLong";
// Создание JWT
String token = Jwts.builder()
.setSubject(user.getUsername()) // Идентификатор пользователя
.claim("roles", user.getRoles()) // Дополнительные claims
.claim("userId", user.getId())
.setIssuedAt(new Date()) // Время создания
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 час
.signWith(SignatureAlgorithm.HS512, secretKey.getBytes()) // Алгоритм подписи
.compact();
// Отправка клиенту
response.setHeader("Authorization", "Bearer " + token);
2. Верификация токена (в каждом защищенном endpoint):
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
public boolean validateToken(String token) {
try {
Jws<Claims> claimsJws = Jwts.parser()
.setSigningKey(secretKey.getBytes())
.parseClaimsJws(token);
Claims claims = claimsJws.getBody();
String username = claims.getSubject();
List<String> roles = claims.get("roles", List.class);
// Проверка expiration
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
// Невалидный токен
return false;
}
}
3. Spring Security интеграция (рекомендуемый способ):
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtAuthenticationFilter jwtFilter() {
return new JwtAuthenticationFilter();
}
}
// Кастомный фильтр для JWT
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
if (jwtUtil.validateToken(token)) {
Authentication auth = jwtUtil.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}
4. Best Practices и безопасность:
- Хранение секретного ключа: Используйте environment variables или специализированные хранилища (HashiCorp Vault, AWS Secrets Manager)
- Алгоритмы подписи:
HS256/HS512— симметричные (требуют общего секрета)RS256/RS512— асимметричные (private key для подписи, public для проверки)
- Refresh tokens: Для продления сессии без повторного логина
{ "access_token": "eyJ...", "refresh_token": "def...", "expires_in": 3600 } - Защита от компрометации:
- Короткое время жизни access token (15-60 минут)
- Blacklist токенов при logout
- HTTPS обязателен
- Проверка issuer (
iss) и audience (aud)
5. Альтернативы и дополнения:
- OAuth 2.0 — полный протокол авторизации (JWT часто используется как access token)
- OpenID Connect — идентификация поверх OAuth 2.0
- Spring Security OAuth2 — готовая реализация для Spring приложений