Authentification de base

Un middleware qui fournit une prise en charge de l'authentification de base.

Introduction à l'authentification de base

L'authentification de base est un mécanisme simple d'authentification HTTP qui transmet un nom d'utilisateur et un mot de passe via l'en-tête Authorization dans les requêtes HTTP. Le format est Authorization: Basic <nom d'utilisateur:mot de passe en base64>. Bien que simple, puisque les informations d'identification sont uniquement encodées en Base64 plutôt que chiffrées, elle est généralement utilisée avec HTTPS pour garantir la sécurité.

Comparaison des implémentations d'authentification de base dans les frameworks courants

FrameworkLangageImplémentation de l'authentification de base
SalvoRustImplémentée via le middleware BasicAuth et le BasicAuthValidator personnalisé
ExpressJavaScriptUtilise le package middleware express-basic-auth
Spring SecurityJavaConfigurée via httpBasic() et implémentée avec UserDetailsService
ASP.NET CoreC#Utilise .UseAuthentication() et AddAuthentication(AuthenticationSchemes.Basic)
GinGoImplémentée via un middleware personnalisé ou en utilisant le package gin-contrib/auth

Cas d'utilisation

L'authentification de base est adaptée aux scénarios suivants :

  1. API et outils internes : Outils de gestion et API utilisés au sein d'une entreprise
  2. Environnements de développement et de test : Mettre rapidement en place une authentification sans système de connexion complexe
  3. Protection simple d'API : Lorsqu'un système complexe de gestion des utilisateurs n'est pas requis
  4. Combinée avec d'autres mesures de sécurité : Comme partie d'une architecture de sécurité multicouche

Dans Salvo, le middleware d'authentification de base peut être facilement intégré aux routes. En implémentant le trait BasicAuthValidator, une logique de validation personnalisée peut être définie, offrant une grande flexibilité.

Considérations

  • Toujours utiliser avec HTTPS pour protéger la transmission des informations d'identification
  • Non adapté aux environnements de production stockant des informations sensibles
  • Envisager d'utiliser des méthodes d'authentification plus sécurisées telles que JWT ou OAuth pour les environnements de production
main.rs
Cargo.toml
use salvo::basic_auth::{BasicAuth, BasicAuthValidator};
use salvo::prelude::*;

// Custom validator implementing BasicAuthValidator trait
struct Validator;
impl BasicAuthValidator for Validator {
    // Validate username and password combination
    async fn validate(&self, username: &str, password: &str, _depot: &mut Depot) -> bool {
        username == "root" && password == "pwd"
    }
}

// Simple handler that returns "Hello" for authenticated requests
#[handler]
async fn hello() -> &'static str {
    "Hello"
}

// Create router with basic authentication middleware
fn route() -> Router {
    // Initialize basic authentication handler with our validator
    let auth_handler = BasicAuth::new(Validator);
    // Apply authentication middleware to the router
    Router::with_hoop(auth_handler).goal(hello)
}

#[tokio::main]
async fn main() {
    // Initialize logging
    tracing_subscriber::fmt().init();

    // Bind server to port 8698 and start serving
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(route()).await;
}

#[cfg(test)]
mod tests {
    use salvo::prelude::*;
    use salvo::test::{ResponseExt, TestClient};

    #[tokio::test]
    async fn test_basic_auth() {
        // Create a service instance from our router for testing purposes
        let service = Service::new(super::route());

        // Test case 1: Verify successful authentication with valid credentials
        let content = TestClient::get("http://0.0.0.0:8698/")
            .basic_auth("root", Some("pwd")) // Use correct username/password
            .send(&service) // Send the request to the service
            .await
            .take_string() // Extract response body as string
            .await
            .unwrap();
        // Verify response contains expected "Hello" message
        assert!(content.contains("Hello"));

        // Test case 2: Verify authentication failure with invalid password
        let content = TestClient::get("http://0.0.0.0:8698/")
            .basic_auth("root", Some("pwd2")) // Use incorrect password
            .send(&service) // Send the request to the service
            .await
            .take_string() // Extract response body as string
            .await
            .unwrap();
        // Verify response contains "Unauthorized" error
        assert!(content.contains("Unauthorized"));
    }
}