JWT токены в Rust: библиотеки и реализация аутентификации

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

JWT токены в Rust: библиотеки и реализация аутентификации

JSON Web Token (JWT) — это открытый стандарт для создания токенов доступа, основанный на формате JSON. В экосистеме Rust работа с JWT реализована через несколько качественных библиотек, среди которых лидирует jsonwebtoken. Этот инструмент обеспечивает полный цикл работы с токенами: создание, подписание, верификацию и декодирование.

Основные библиотеки для JWT в Rust

На текущий момент наиболее популярные крейты для работы с JWT:

  • jsonwebtoken — основная библиотека с поддержкой HS256, RS256, EdDSA алгоритмов
  • jwt — более легковесная альтернатива с базовым функционалом
  • biscuit — продвинутая библиотека с поддержкой сложных сценариев

В этой статье мы сфокусируемся на jsonwebtoken как на самом зрелом и документированном решении.

Установка и настройка

Добавьте зависимость в Cargo.toml:

[dependencies]
jsonwebtoken = "9.3"
serde = { version = "1.0", features = ["derive"] }
chrono = "0.4"

Создание и верификация JWT токенов

Рассмотрим полный пример реализации аутентификации с JWT:

use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};
use serde::{Serialize, Deserialize};
use chrono::{Utc, Duration};

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,        // идентификатор пользователя
    exp: usize,         // время истечения токена
    iat: usize,         // время создания токена
    role: String,       // роль пользователя
}

fn create_token(user_id: &str, role: &str, secret: &str) -> Result {
    let now = Utc::now();
    let claims = Claims {
        sub: user_id.to_string(),
        exp: (now + Duration::hours(24)).timestamp() as usize,
        iat: now.timestamp() as usize,
        role: role.to_string(),
    };
    
    encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret(secret.as_bytes())
    )
}

fn verify_token(token: &str, secret: &str) -> Result {
    let token_data = decode::(
        token,
        &DecodingKey::from_secret(secret.as_bytes()),
        &Validation::default()
    )?;
    Ok(token_data.claims)
}

Практическое использование в веб-приложении

Интеграция JWT с веб-фреймворками (например, Actix-web или Axum) требует создания middleware для проверки токенов:

use actix_web::{HttpRequest, HttpResponse, web, App, HttpServer, middleware::from_fn};
use std::sync::Arc;

// Middleware для проверки JWT
async fn auth_middleware(
    req: HttpRequest,
) -> Result {
    let auth_header = req.headers()
        .get("Authorization")
        .and_then(|v| v.to_str().ok())
        .ok_or(HttpResponse::Unauthorized().json("Missing token"))?;
    
    let token = auth_header
        .strip_prefix("Bearer ")
        .ok_or(HttpResponse::Unauthorized().json("Invalid token format"))?;
    
    verify_token(token, SECRET)
        .map_err(|_| HttpResponse::Unauthorized().json("Invalid token"))
}

// Защищенный эндпоинт
async fn protected_endpoint(claims: Claims) -> HttpResponse {
    HttpResponse::Ok().json(claims)
}

Расширенные возможности и лучшие практики

  • Refresh токены — используйте короткоживущие access токены (15-30 минут) и долгоживущие refresh токены (7-30 дней)
  • Хранение секретов — никогда не храните секретные ключи в коде, используйте переменные окружения или безопасное хранилище
  • Алгоритмы подписи — для продакшена используйте RS256 (асимметричное шифрование) вместо HS256
  • Валидация claims — всегда проверяйте exp, iss, aud поля

Пример с асимметричным шифрованием (RS256)

use jsonwebtoken::{encode, decode, Header, Validation, 
    EncodingKey, DecodingKey, Algorithm};

fn create_rsa_token(user_id: &str, private_key_pem: &str) -> Result {
    let claims = Claims { /* ... */ };
    encode(
        &Header::new(Algorithm::RS256),
        &claims,
        &EncodingKey::from_rsa_pem(private_key_pem.as_bytes())?
    )
}

fn verify_rsa_token(token: &str, public_key_pem: &str) -> Result {                    

Рекомендаций