Grundlegende Authentifizierung

Middleware zur Unterstützung von Basic Auth.

Einführung in Basic Auth

Basic Auth ist ein einfacher HTTP-Authentifizierungsmechanismus, bei dem Benutzername und Passwort über das Authorization-Feld im HTTP-Header übertragen werden. Das Format lautet Authorization: Basic <base64(username:password)>. Obwohl einfach, sollten die Anmeldeinformationen aufgrund der nur Base64-kodierten (nicht verschlüsselten) Übertragung stets zusammen mit HTTPS verwendet werden, um die Sicherheit zu gewährleisten.

Vergleich der Basic Auth-Implementierungen in gängigen Frameworks

FrameworkSpracheBasic Auth-Implementierung
SalvoRustDurch BasicAuth-Middleware und benutzerdefinierten BasicAuthValidator
ExpressJavaScriptVerwendung des express-basic-auth-Middleware-Pakets
Spring SecurityJavaKonfiguration über httpBasic() und Implementierung von UserDetailsService
ASP.NET CoreC#Verwendung von .UseAuthentication() und AddAuthentication(AuthenticationSchemes.Basic)
GinGoBenutzerdefinierte Middleware oder das gin-contrib/auth-Paket

Anwendungsfälle

Basic Auth eignet sich für folgende Szenarien:

  1. Interne APIs und Tools: Unternehmensinterne Verwaltungstools und APIs
  2. Entwicklungs- und Testumgebungen: Schnelle Authentifizierung ohne komplexe Anmeldesysteme
  3. Einfacher API-Schutz: Wenn kein komplexes Benutzerverwaltungssystem benötigt wird
  4. Kombination mit anderen Sicherheitsmaßnahmen: Als Teil einer mehrschichtigen Sicherheitsarchitektur

In Salvo lässt sich die Basic Auth-Middleware einfach in Routen integrieren. Durch die Implementierung des BasicAuthValidator-Traits kann die Validierungslogik flexibel angepasst werden.

Wichtige Hinweise

  • Immer in Kombination mit HTTPS verwenden, um die Übertragung der Anmeldeinformationen zu schützen
  • Nicht für Produktionsumgebungen mit sensiblen Daten geeignet
  • Für Produktionsumgebungen sollten sicherere Authentifizierungsmethoden wie JWT oder OAuth in Betracht gezogen werden
main.rs
Cargo.toml
basic-auth/src/main.rs
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 5800 and start serving
    let acceptor = TcpListener::new("0.0.0.0:5800").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:5800/")
            .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:5800/")
            .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"));
    }
}