Чья подпись в токене: разработчика или пользователя
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Подпись в JWT токене
JWT (JSON Web Token) содержит подпись, которая выдается сервером (разработчиком), а не пользователем. Подпись создается на основе секретного ключа, который хранится только на сервере и никогда не передается клиенту.
Как работает подпись в JWT
JWT состоит из трех частей, разделенных точками:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
[Header].[Payload].[Signature]
- Header — информация о типе токена и алгоритме подписи
- Payload — данные о пользователе (username, id, roles и т.д.)
- Signature — подпись, созданная на сервере
Кто создает подпись
Подпись создает сервер при генерации токена:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
public class JwtTokenProvider {
private String secretKey = "your-secret-key-keep-it-safe-on-server"; // Хранится только на сервере!
private long expirationTime = 3600000; // 1 час
public String generateToken(String username) {
// Сервер создает подпись с помощью своего секретного ключа
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expirationTime))
.signWith(SignatureAlgorithm.HS256, secretKey) // Подпись создается здесь
.compact();
}
}
Процесс подписания
- Сервер берет Header (закодированный в Base64)
- Берет Payload (закодированный в Base64)
- Конкатенирует их:
header.payload - Вычисляет HMAC-SHA256 (или другой алгоритм) с секретным ключом:
SIGNATURE = HMAC-SHA256(header.payload, SECRET_KEY)
- Результат кодируется в Base64 и присоединяется третьей частью
Кто может проверить подпись
Только тот, у кого есть секретный ключ может проверить подпись:
public boolean validateToken(String token) {
try {
// Сервер проверяет подпись, используя тот же секретный ключ
Jwts.parser()
.setSigningKey(secretKey) // Требуется секретный ключ
.parseClaimsJws(token); // Проверяет подпись
return true;
} catch (JwtException e) {
return false; // Подпись невалидна
}
}
Почему пользователь не может создать подпись
Пользователь не может создать корректную подпись потому, что:
- Секретный ключ хранится только на сервере — пользователь его не знает
- Пользователь не может подделать подпись — без ключа невозможно вычислить правильный HMAC
- Если пользователь попытается изменить Payload (например, добавить себе админ-права), подпись перестанет совпадать
Пример попытки подделки
// Легитимный токен от сервера
String validToken = jwtProvider.generateToken("john");
// Payload: {"username": "john", "role": "user"}
// Пользователь пытается изменить payload
String hackedPayload = Base64.encode("{\"username\": \"john\", \"role\": \"admin\"}");
// Новый токен: header.hackedPayload.oldSignature
// РЕЗУЛЬТАТ: Подпись не совпадет! Сервер отклонит токен.
Асимметричная криптография (RSA)
В некоторых случаях используется асимметричная криптография:
public String generateTokenWithRSA(String username, PrivateKey privateKey) {
return Jwts.builder()
.setSubject(username)
.signWith(SignatureAlgorithm.RS256, privateKey) // Подписываем приватным ключом
.compact();
}
public boolean validateTokenWithRSA(String token, PublicKey publicKey) {
try {
Jwts.parser()
.setSigningKey(publicKey) // Проверяем публичным ключом
.parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}
В этом случае:
- Сервер подписывает приватным ключом (есть только у него)
- Клиент проверяет публичным ключом (может быть распространен)
- Пользователь все равно не может создать валидную подпись
Практическое применение в Spring
@RestController
@RequestMapping("/api")
public class AuthController {
@Autowired
private JwtTokenProvider tokenProvider;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// Проверяем учетные данные
// ...
// Сервер создает подписанный токен
String token = tokenProvider.generateToken(request.getUsername());
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
@GetMapping("/secure")
public ResponseEntity<?> secureEndpoint(@RequestHeader("Authorization") String token) {
// Сервер проверяет подпись перед доступом
if (!tokenProvider.validateToken(token)) {
return ResponseEntity.status(401).build();
}
return ResponseEntity.ok("Access granted");
}
}
Заключение
Подпись в JWT создает и проверяет сервер (разработчик приложения), используя свой секретный ключ. Пользователь не может подделать подпись, так как не имеет доступа к этому ключу. Это обеспечивает безопасность и целостность токена в течение всего его жизненного цикла.