Волга
Главная
API Docs
GitHub
  • English
  • Русский
Главная
API Docs
GitHub
  • English
  • Русский
  • Главная
  • Основы

    • Быстрый старт
    • Параметры маршрута
    • Параметры запроса
    • Группировка маршрутов
    • Заголовки (Headers)
    • Основы Middleware
  • Форматы данных

    • Работа с JSON
    • Работа с Form Data
    • Работа с файлами
    • Server-Sent Events (SSE)
  • Протоколы

    • HTTP/1 and HTTP/2
    • HTTPS
    • WebSockets & WebTransport
  • Продвинутые сценарии

    • Пользовательские Middleware
    • Сжатие ответов
    • Распаковка запросов
    • Центральный обработчик ошибок
    • Внедрение Зависимостей
    • Логгирование и Трассировка
    • Статические файлы
    • CORS (Cross-Origin Resource Sharing)
    • Cookies
    • Аутентификация и авторизация
    • Отмена запросов
    • Пользовательская обработка методов HEAD, OPTIONS и TRACE

Аутентификация и авторизация

Волга предоставляет гибкие инструменты для реализации аутентификации и авторизации в веб-приложениях. Поддерживаются как базовая аутентификация (Basic Auth), так и токены доступа на основе JWT (Bearer Token). Также доступна система контроля доступа по ролям, разрешениям или пользовательским правилам.

Базовая аутентификация (Basic Auth)

Базовая аутентификация — это простой механизм проверки логина и пароля через HTTP-заголовок Authorization: Basic.

Зависимости

[dependencies]
volga = { version = "0.6.1", features = ["basic-auth"] }

Пример

use volga::{
    App, HttpResult,
    headers::WWW_AUTHENTICATE,
    auth::Basic,
    status, ok
};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut app = App::new();
    app.map_get("/protected", protected);
    app.run().await
}

async fn protected(auth: Basic) -> HttpResult {
    let (expected_user, expected_pass) = get_credentials_from_db().await;
    if auth.validate(&expected_user, &expected_pass) {
        ok!("Access granted")
    } else {
        status!(401, "Unauthorized", [
            (WWW_AUTHENTICATE, "Basic realm=\"Restricted area\"")
        ])
    }
}

async fn get_credentials_from_db() -> (String, String) {
    // В реальном приложении читаем данные из БД или хранилища
    ("foo".into(), "bar".into())
}

Структура Basic автоматически извлекает заголовок авторизации и предоставляет методы validate() и validate_base64() для проверки логина и пароля.

Аутентификация через JWT (Bearer Token)

JWT (JSON Web Token) предоставляет расширенные возможности: аутентификацию с данными (claims) и авторизацию по ролям и правам доступа.

Зависимости

[dependencies]
volga = { version = "0.6.1", features = ["jwt-auth-full"] }

Генерация токена

use std::{ops::Add, time::{SystemTime, UNIX_EPOCH, Duration}};
use serde::{Serialize, Deserialize};
use volga::{
    App, Json, HttpResult,
    auth::{Claims, BearerTokenService, EncodingKey},
    ok, status, bad_request
};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let secret = std::env::var("JWT_SECRET")
        .expect("JWT_SECRET must be set");

    let mut app = App::new()
        .with_bearer_auth(|auth| {
            auth.set_encoding_key(EncodingKey::from_secret(secret.as_bytes()))
        });

    app.map_post("/generate", generate);
    app.run().await
}

async fn generate(payload: Json<Payload>, bts: BearerTokenService) -> HttpResult {
    if payload.client_id != "foo" || payload.client_secret != "bar" {
        return status!(401, "Invalid credentials");
    }

    let exp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .add(Duration::from_secs(300))
        .as_secs();

    let claims = Claims {
        sub: "user@email.com".into(),
        company: "Awesome Co.".into(),
        role: "admin".into(),
        exp,
    };

    let token = bts.encode(&claims)?.to_string();
    ok!(AuthData { access_token: token })
}

#[derive(Claims, Serialize, Deserialize)]
struct Claims {
    sub: String,
    company: String,
    role: String,
    exp: u64,
}

#[derive(Serialize)]
struct AuthData {
    access_token: String,
}

#[derive(Deserialize)]
struct Payload {
    client_id: String,
    client_secret: String,
}

Использование JWT

Промежуточное ПО authorize() предоставляет инструменты для реализации управления доступом на основе ролей или разрешений и может быть определено для отдельного маршрута, группы маршрутов или всего приложения.

use serde::Deserialize;
use volga::{
    App, ok,
    auth::{Claims, DecodingKey, roles},
};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let secret = std::env::var("JWT_SECRET")
        .expect("JWT_SECRET must be set");

    let mut app = App::new()
        .with_bearer_auth(|auth| {
            auth.set_decoding_key(DecodingKey::from_secret(secret.as_bytes()))
        });

    app.map_get("/me", me)
        .authorize::<Claims>(roles(["admin", "user"]));

    app.run().await
}

async fn me() -> &'static str {
    "Hello from protected route"
}

#[derive(Claims, Deserialize)]
struct Claims {
    sub: String,
    company: String,
    role: String,
    exp: u64,
}

Примечание

Для корректной работы JWT-аутентификации, необходимо использовать один и тот же JWT_SECRET как при генерации токена, так и при его проверке на защищённых маршрутах. Этот секрет используется для подписания токена на этапе генерации и проверки подписи на этапе валидации. Если секреты отличаются - токен будет отклонён как недействительный.

Определение структуры Claims

Для удобства jwt-auth-full включает derive-макрос Claims для объявления структуры claim'ов. Но вы также можете использовать альтернативные способы:

Макрос claims!

use volga::auth::claims;

claims! {
    #[derive(Deserialize)]
    struct Claims {
        sub: String,
        role: String,
        permissions: Vec<String>,
    }
}

Ручная реализация

use volga::auth::AuthClaims;

#[derive(Deserialize)]
struct Claims {
    sub: String,
    role: String,
    permissions: Vec<String>,
}

impl AuthClaims for Claims {
    fn role(&self) -> Option<&str> {
        Some(&self.role)
    }

    fn permissions(&self) -> Option<&[String]> {
        Some(&self.permissions)
    }
}

Декларативный контроль доступа с Authorizer

Волга предоставляет гибкую систему контроля доступа с помощью Authorizer.

Вы можете проверять доступ по:

  • role("admin") - одной роли.
  • roles(["admin", "user"]) - по списку ролей.
  • permission("write") - по одному разрешению.
  • permissions(["read", "write"]) - по списку разрешений.
  • predicate(|claims| ...) - кастомная логика.

Пример

use volga::auth::{AuthClaims, role, roles, permission, permissions, predicate, Authorizer};
use serde::Deserialize;

#[derive(Deserialize)]
struct MyClaims {
    role: String,
    permissions: Vec<String>,
}

impl AuthClaims for MyClaims {
    fn role(&self) -> Option<&str> {
        Some(&self.role)
    }

    fn permissions(&self) -> Option<&[String]> {
        Some(&self.permissions)
    }
}

fn main() {
    let admin = role("admin");
    let editors = roles(["editor", "contributor"]);
    let can_write = permission("write");

    let access_policy = admin.or(editors).and(can_write);

    let user = MyClaims {
        role: "editor".into(),
        permissions: vec!["write".into()],
    };

    assert!(access_policy.validate(&user));
}

Совет

Вы также можете объединять правила в цепочку с помощью комбинаторов and() и or() для создания комплексных правил.

Примеры

  • Basic Auth
  • JWT
Последнее обновление: 27.07.2025, 08:43
Prev
Cookies
Next
Отмена запросов