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

Кто подписывает выданный разработчиком токен

1.7 Middle🔥 151 комментариев
#REST API и микросервисы#Безопасность

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

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

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

Кто подписывает выданный разработчиком токен

Это вопрос о токены-базированной аутентификации, главным образом 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);
    }
}

Таблица: Кто что может делать

ОперацияClientServer
Создать токен❌ Не может (нет ключа)✅ Может (есть ключ)
Подписать токен❌ Не может✅ Может
Изменить payload❌ Подпись станет неверна✅ Может (но нежелательно)
Проверить подпись✅ Может (есть публичный ключ)✅ Может (есть приватный ключ)
Подделать подпись❌ Не может (не знает ключ)N/A

Вывод

Подписывает токен только тот сервер, который владеет приватным ключом — обычно сервис аутентификации. Это гарантирует, что:

  1. Токен подлинный — не подделан
  2. Токен не изменен — клиент не может модифицировать данные
  3. Источник известен — сервер знает, кто выдал токен

Это основа безопасности JWT и токен-базированной аутентификации.

Кто подписывает выданный разработчиком токен | PrepBro