← Назад к вопросам

Чья подпись в токене: разработчика или пользователя

1.0 Junior🔥 71 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Подпись в JWT токене

JWT (JSON Web Token) содержит подпись, которая выдается сервером (разработчиком), а не пользователем. Подпись создается на основе секретного ключа, который хранится только на сервере и никогда не передается клиенту.

Как работает подпись в JWT

JWT состоит из трех частей, разделенных точками:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

[Header].[Payload].[Signature]
  1. Header — информация о типе токена и алгоритме подписи
  2. Payload — данные о пользователе (username, id, roles и т.д.)
  3. 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();
    }
}

Процесс подписания

  1. Сервер берет Header (закодированный в Base64)
  2. Берет Payload (закодированный в Base64)
  3. Конкатенирует их: header.payload
  4. Вычисляет HMAC-SHA256 (или другой алгоритм) с секретным ключом:
SIGNATURE = HMAC-SHA256(header.payload, SECRET_KEY)
  1. Результат кодируется в Base64 и присоединяется третьей частью

Кто может проверить подпись

Только тот, у кого есть секретный ключ может проверить подпись:

public boolean validateToken(String token) {
    try {
        // Сервер проверяет подпись, используя тот же секретный ключ
        Jwts.parser()
            .setSigningKey(secretKey)  // Требуется секретный ключ
            .parseClaimsJws(token);    // Проверяет подпись
        return true;
    } catch (JwtException e) {
        return false; // Подпись невалидна
    }
}

Почему пользователь не может создать подпись

Пользователь не может создать корректную подпись потому, что:

  1. Секретный ключ хранится только на сервере — пользователь его не знает
  2. Пользователь не может подделать подпись — без ключа невозможно вычислить правильный HMAC
  3. Если пользователь попытается изменить 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 создает и проверяет сервер (разработчик приложения), используя свой секретный ключ. Пользователь не может подделать подпись, так как не имеет доступа к этому ключу. Это обеспечивает безопасность и целостность токена в течение всего его жизненного цикла.

Чья подпись в токене: разработчика или пользователя | PrepBro