Кто подписывает выданный разработчиком токен
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Кто подписывает выданный разработчиком токен
Это вопрос о токены-базированной аутентификации, главным образом JWT (JSON Web Token). Ответ: сервер, владеющий приватным ключом.
Архитектура подписи токена
1. Разработчик выдает токен
public class TokenService {
private final String SECRET_KEY = "super-secret-key-never-hardcode-this";
public String generateToken(User user) {
// Создаем payload (данные пользователя)
Claims claims = Jwts.claims()
.setSubject(user.getId())
.set("username", user.getUsername())
.set("roles", user.getRoles());
// Подписываем токен нашим приватным ключом
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // ПОДПИСЬ
.compact();
}
}
Важно: Подпись создает только тот, кто владеет приватным ключом (в данном случае — сервер).
2. Клиент отправляет токен
// Клиент (браузер, мобильное приложение)
public class ApiClient {
public void callAPI(String token) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
// Отправляем токен серверу
// GET /api/v1/protected
// Header: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
}
}
3. Сервер проверяет подпись
public class JwtValidator {
private final String SECRET_KEY = "super-secret-key-never-hardcode-this";
public Claims validateToken(String token) {
try {
// Проверяем подпись, используя ТОТ ЖЕ приватный ключ
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY) // ПРОВЕРКА
.build()
.parseClaimsJws(token) // Выбросит исключение, если подпись неверна
.getBody();
} catch (JwtException e) {
throw new UnauthorizedException("Invalid token signature");
}
}
}
Ключевой момент: кто подписывает?
Ответ: Сервер, выдавший токен, подписывает его своим приватным ключом.
Это означает:
- Только сервер может создать валидный токен
- Клиент не может создать токен (не знает приватный ключ)
- Клиент не может изменить содержимое токена (изменение = неверная подпись)
- Клиент не может подделать чужой токен
Асимметричная криптография (RSA)
Для большей безопасности используют публичный и приватный ключи:
public class RSATokenService {
private PrivateKey privateKey; // Только на сервере
public String generateToken(User user) {
return Jwts.builder()
.setSubject(user.getId())
.signWith(SignatureAlgorithm.RS256, privateKey) // Подписываем приватным ключом
.compact();
}
}
public class JwtValidator {
private PublicKey publicKey; // Может быть везде (в Интернете)
public Claims validateToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(publicKey) // Проверяем публичным ключом
.build()
.parseClaimsJws(token)
.getBody();
}
}
Преимущество RSA: Публичный ключ можно распространять, приватный ключ остается в секрете.
Сценарий: Микросервисная архитектура
┌─────────────────┐
│ Auth Service │ Подписывает токен приватным ключом
│ (HS256: secret) │
└────────┬────────┘
│ Выдает Token
│
┌────────▼────────┐
│ Client │ Отправляет Token в Header
└────────┬────────┘
│
┌────────▼────────────────┐
│ API Service 1, 2, 3 │ Проверяют подпись
│ (знают secret или key) │ с ТАВМ же ключом
└─────────────────────────┘
Важно: Все сервисы должны знать ключ подписи для проверки. Если используется RSA, то ключ можно распространить, но приватный ключ остается только на Auth сервисе.
Опасность: неверное хранение ключа
// ❌ ОПАСНО: ключ в исходном коде
public class BadTokenService {
private static final String SECRET_KEY = "my-secret-123"; // Видно в GitHub
}
// ❌ ОПАСНО: ключ неправильно экранирован
private String SECRET_KEY = "my-\\nsecret"; // Работает неправильно
// ✅ ПРАВИЛЬНО: ключ в переменных окружения
private String SECRET_KEY = System.getenv("JWT_SECRET_KEY");
// ✅ ПРАВИЛЬНО: ключ в защищённом хранилище
private String SECRET_KEY = secretsManager.getSecret("jwt-key");
Практический пример с Spring Security
@Configuration
public class SecurityConfig {
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtValidator validator;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = extractTokenFromHeader(request);
if (token != null) {
try {
// Проверяем подпись (сервер знает ключ)
Claims claims = validator.validateToken(token);
// Создаем Authentication объект
Authentication auth = new UsernamePasswordAuthenticationToken(
claims.getSubject(),
null,
parseAuthorities(claims)
);
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (JwtException e) {
// Подпись неверна — не авторизуем
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
}
filterChain.doFilter(request, response);
}
}
Таблица: Кто что может делать
| Операция | Client | Server |
|---|---|---|
| Создать токен | ❌ Не может (нет ключа) | ✅ Может (есть ключ) |
| Подписать токен | ❌ Не может | ✅ Может |
| Изменить payload | ❌ Подпись станет неверна | ✅ Может (но нежелательно) |
| Проверить подпись | ✅ Может (есть публичный ключ) | ✅ Может (есть приватный ключ) |
| Подделать подпись | ❌ Не может (не знает ключ) | N/A |
Вывод
Подписывает токен только тот сервер, который владеет приватным ключом — обычно сервис аутентификации. Это гарантирует, что:
- Токен подлинный — не подделан
- Токен не изменен — клиент не может модифицировать данные
- Источник известен — сервер знает, кто выдал токен
Это основа безопасности JWT и токен-базированной аутентификации.