Autenticación Básica

Middleware que proporciona soporte para Basic Auth.

Introducción a Basic Auth

Basic Auth es un mecanismo simple de autenticación HTTP que transmite credenciales a través del encabezado Authorization en las solicitudes HTTP. El formato es Authorization: Basic <base64(username:password)>. Aunque sencillo, como las credenciales solo están codificadas en Base64 y no encriptadas, generalmente debe usarse con HTTPS para garantizar la seguridad.

Comparación de implementaciones de Basic Auth en frameworks comunes

FrameworkLenguajeImplementación de Basic Auth
SalvoRustMediante middleware BasicAuth e implementación personalizada de BasicAuthValidator
ExpressJavaScriptUsando el paquete middleware express-basic-auth
Spring SecurityJavaConfiguración con httpBasic() e implementación de UserDetailsService
ASP.NET CoreC#Usando .UseAuthentication() y AddAuthentication(AuthenticationSchemes.Basic)
GinGoA través de middleware personalizado o usando el paquete gin-contrib/auth

Casos de uso

Basic Auth es adecuado para los siguientes escenarios:

  1. APIs y herramientas internas: Herramientas administrativas y APIs de uso interno en empresas
  2. Entornos de desarrollo y pruebas: Implementación rápida de autenticación sin sistemas de login complejos
  3. Protección simple de APIs: Cuando no se requiere un sistema complejo de gestión de usuarios
  4. En combinación con otras medidas de seguridad: Como parte de una arquitectura de seguridad multicapa

En Salvo, el middleware Basic Auth puede integrarse fácilmente en las rutas, permitiendo personalizar la lógica de validación implementando el trait BasicAuthValidator, lo que lo hace muy flexible.

Consideraciones importantes

  • Siempre usar con HTTPS para proteger la transmisión de credenciales
  • No es adecuado para entornos de producción que manejen información sensible
  • Considerar usar métodos de autenticación más seguros como JWT, OAuth, etc. para entornos de producción
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"));
    }
}